]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/iD/iD.js
Update to iD v2.18.1
[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             // The client app may want to manually set the locale, regardless of the
18296             // settings provided by the browser
18297             let _preferredLocaleCodes = [];
18298             localizer.preferredLocaleCodes = function(codes) {
18299                 if (!arguments.length) return _preferredLocaleCodes;
18300                 if (typeof codes === 'string') {
18301                     // be generous and accept delimited strings as input
18302                     _preferredLocaleCodes = codes.split(/,|;| /gi).filter(Boolean);
18303                 } else {
18304                     _preferredLocaleCodes = codes;
18305                 }
18306                 return localizer;
18307             };
18308
18309
18310             var _loadPromise;
18311
18312             localizer.ensureLoaded = () => {
18313
18314                 if (_loadPromise) return _loadPromise;
18315
18316                 return _loadPromise = Promise.all([
18317                         // load the list of langauges
18318                         _mainFileFetcher.get('languages'),
18319                         // load the list of supported locales
18320                         _mainFileFetcher.get('locales')
18321                     ])
18322                     .then(results => {
18323                         _dataLanguages = results[0];
18324                         _dataLocales = results[1];
18325                     })
18326                     .then(() => {
18327                         let requestedLocales = (_preferredLocaleCodes || [])
18328                             // list of locales preferred by the browser in priority order
18329                             .concat(utilDetect().browserLocales);
18330                         _localeCode = bestSupportedLocale(requestedLocales);
18331
18332                         return Promise.all([
18333                             // always load the English locale strings as fallbacks
18334                             localizer.loadLocale('en'),
18335                             // load the preferred locale
18336                             localizer.loadLocale(_localeCode)
18337                         ]);
18338                     })
18339                     .then(() => {
18340                         updateForCurrentLocale();
18341                     })
18342                     .catch(err => console.error(err));  // eslint-disable-line
18343             };
18344
18345             // Returns the best locale from `locales` supported by iD, if any
18346             function bestSupportedLocale(locales) {
18347                 let supportedLocales = _dataLocales;
18348
18349                 for (let i in locales) {
18350                     let locale = locales[i];
18351                     if (locale.includes('-')) { // full locale ('es-ES')
18352
18353                         if (supportedLocales[locale]) return locale;
18354
18355                         // If full locale not supported ('es-FAKE'), fallback to the base ('es')
18356                         let langPart = locale.split('-')[0];
18357                         if (supportedLocales[langPart]) return langPart;
18358
18359                     } else { // base locale ('es')
18360
18361                         // prefer a lower-priority full locale with this base ('es' < 'es-ES')
18362                         let fullLocale = locales.find((locale2, index) => {
18363                             return index > i &&
18364                                 locale2 !== locale &&
18365                                 locale2.split('-')[0] === locale &&
18366                                 supportedLocales[locale2];
18367                         });
18368                         if (fullLocale) return fullLocale;
18369
18370                         if (supportedLocales[locale]) return locale;
18371                     }
18372                 }
18373
18374                 return null;
18375             }
18376
18377             function updateForCurrentLocale() {
18378                 if (!_localeCode) return;
18379
18380                 _languageCode = _localeCode.split('-')[0];
18381
18382                 const currentData = _dataLocales[_localeCode] || _dataLocales[_languageCode];
18383
18384                 const hash = utilStringQs(window.location.hash);
18385
18386                 if (hash.rtl === 'true') {
18387                     _textDirection = 'rtl';
18388                 } else if (hash.rtl === 'false') {
18389                     _textDirection = 'ltr';
18390                 }  else {
18391                     _textDirection = currentData && currentData.rtl ? 'rtl' : 'ltr';
18392                 }
18393
18394                 _languageNames = currentData && currentData.languageNames;
18395                 _scriptNames = currentData && currentData.scriptNames;
18396
18397                 _usesMetric = _localeCode.slice(-3).toLowerCase() !== '-us';
18398             }
18399
18400
18401             /* Locales */
18402             // Returns a Promise to load the strings for the requested locale
18403             localizer.loadLocale = (requested) => {
18404
18405                 if (!_dataLocales) {
18406                     return Promise.reject('loadLocale called before init');
18407                 }
18408
18409                 let locale = requested;
18410
18411                 // US English is the default
18412                 if (locale.toLowerCase() === 'en-us') locale = 'en';
18413
18414                 if (!_dataLocales[locale]) {
18415                     return Promise.reject(`Unsupported locale: ${requested}`);
18416                 }
18417
18418                 if (_localeStrings[locale]) {    // already loaded
18419                     return Promise.resolve(locale);
18420                 }
18421
18422                 let fileMap = _mainFileFetcher.fileMap();
18423                 const key = `locale_${locale}`;
18424                 fileMap[key] = `locales/${locale}.json`;
18425
18426                 return _mainFileFetcher.get(key)
18427                     .then(d => {
18428                         _localeStrings[locale] = d[locale];
18429                         return locale;
18430                     });
18431             };
18432
18433             /**
18434             * Given a string identifier, try to find that string in the current
18435             * language, and return it.  This function will be called recursively
18436             * with locale `en` if a string can not be found in the requested language.
18437             *
18438             * @param  {string}   s             string identifier
18439             * @param  {object?}  replacements  token replacements and default string
18440             * @param  {string?}  locale        locale to use (defaults to currentLocale)
18441             * @return {string?}  localized string
18442             */
18443             localizer.t = function(s, replacements, locale) {
18444                 locale = locale || _localeCode;
18445
18446                 // US English is the default
18447                 if (locale.toLowerCase() === 'en-us') locale = 'en';
18448
18449                 let path = s
18450                   .split('.')
18451                   .map(s => s.replace(/<TX_DOT>/g, '.'))
18452                   .reverse();
18453
18454                 let result = _localeStrings[locale];
18455
18456                 while (result !== undefined && path.length) {
18457                   result = result[path.pop()];
18458                 }
18459
18460                 if (result !== undefined) {
18461                   if (replacements) {
18462                     for (let k in replacements) {
18463                       const token = `{${k}}`;
18464                       const regex = new RegExp(token, 'g');
18465                       result = result.replace(regex, replacements[k]);
18466                     }
18467                   }
18468                   return result;
18469                 }
18470
18471                 if (locale !== 'en') {
18472                   return localizer.t(s, replacements, 'en');  // fallback - recurse with 'en'
18473                 }
18474
18475                 if (replacements && 'default' in replacements) {
18476                   return replacements.default;      // fallback - replacements.default
18477                 }
18478
18479                 const missing = `Missing ${locale} translation: ${s}`;
18480                 if (typeof console !== 'undefined') console.error(missing);  // eslint-disable-line
18481
18482                 return missing;
18483             };
18484
18485             localizer.languageName = (code, options) => {
18486
18487                 if (_languageNames[code]) {  // name in locale langauge
18488                   // e.g. "German"
18489                   return _languageNames[code];
18490                 }
18491
18492                 // sometimes we only want the local name
18493                 if (options && options.localOnly) return null;
18494
18495                 const langInfo = _dataLanguages[code];
18496                 if (langInfo) {
18497                   if (langInfo.nativeName) {  // name in native language
18498                     // e.g. "Deutsch (de)"
18499                     return localizer.t('translate.language_and_code', { language: langInfo.nativeName, code: code });
18500
18501                   } else if (langInfo.base && langInfo.script) {
18502                     const base = langInfo.base;   // the code of the langauge this is based on
18503
18504                     if (_languageNames[base]) {   // base language name in locale langauge
18505                       const scriptCode = langInfo.script;
18506                       const script = _scriptNames[scriptCode] || scriptCode;
18507                       // e.g. "Serbian (Cyrillic)"
18508                       return localizer.t('translate.language_and_code', { language: _languageNames[base], code: script });
18509
18510                     } else if (_dataLanguages[base] && _dataLanguages[base].nativeName) {
18511                       // e.g. "српски (sr-Cyrl)"
18512                       return localizer.t('translate.language_and_code', { language: _dataLanguages[base].nativeName, code: code });
18513                     }
18514                   }
18515                 }
18516                 return code;  // if not found, use the code
18517             };
18518
18519             return localizer;
18520         }
18521
18522         //
18523         // `presetCollection` is a wrapper around an `Array` of presets `collection`,
18524         // and decorated with some extra methods for searching and matching geometry
18525         //
18526         function presetCollection(collection) {
18527           const MAXRESULTS = 50;
18528           let _this = {};
18529           let _memo = {};
18530
18531           _this.collection = collection;
18532
18533           _this.item = (id) => {
18534             if (_memo[id]) return _memo[id];
18535             const found = _this.collection.find(d => d.id === id);
18536             if (found) _memo[id] = found;
18537             return found;
18538           };
18539
18540           _this.index = (id) => _this.collection.findIndex(d => d.id === id);
18541
18542           _this.matchGeometry = (geometry) => {
18543             return presetCollection(
18544               _this.collection.filter(d => d.matchGeometry(geometry))
18545             );
18546           };
18547
18548           _this.matchAllGeometry = (geometries) => {
18549             return presetCollection(
18550               _this.collection.filter(d => d && d.matchAllGeometry(geometries))
18551             );
18552           };
18553
18554           _this.matchAnyGeometry = (geometries) => {
18555             return presetCollection(
18556               _this.collection.filter(d => geometries.some(geom => d.matchGeometry(geom)))
18557             );
18558           };
18559
18560           _this.fallback = (geometry) => {
18561             let id = geometry;
18562             if (id === 'vertex') id = 'point';
18563             return _this.item(id);
18564           };
18565
18566           _this.search = (value, geometry, countryCode) => {
18567             if (!value) return _this;
18568
18569             value = value.toLowerCase().trim();
18570
18571             // match at name beginning or just after a space (e.g. "office" -> match "Law Office")
18572             function leading(a) {
18573               const index = a.indexOf(value);
18574               return index === 0 || a[index - 1] === ' ';
18575             }
18576
18577             // match at name beginning only
18578             function leadingStrict(a) {
18579               const index = a.indexOf(value);
18580               return index === 0;
18581             }
18582
18583             function sortNames(a, b) {
18584               let aCompare = (a.suggestion ? a.originalName : a.name()).toLowerCase();
18585               let bCompare = (b.suggestion ? b.originalName : b.name()).toLowerCase();
18586
18587               // priority if search string matches preset name exactly - #4325
18588               if (value === aCompare) return -1;
18589               if (value === bCompare) return 1;
18590
18591               // priority for higher matchScore
18592               let i = b.originalScore - a.originalScore;
18593               if (i !== 0) return i;
18594
18595               // priority if search string appears earlier in preset name
18596               i = aCompare.indexOf(value) - bCompare.indexOf(value);
18597               if (i !== 0) return i;
18598
18599               // priority for shorter preset names
18600               return aCompare.length - bCompare.length;
18601             }
18602
18603             let pool = _this.collection;
18604             if (countryCode) {
18605               pool = pool.filter(a => {
18606                 if (a.countryCodes && a.countryCodes.indexOf(countryCode) === -1) return false;
18607                 if (a.notCountryCodes && a.notCountryCodes.indexOf(countryCode) !== -1) return false;
18608                 return true;
18609               });
18610             }
18611             const searchable = pool.filter(a => a.searchable !== false && a.suggestion !== true);
18612             const suggestions = pool.filter(a => a.suggestion === true);
18613
18614             // matches value to preset.name
18615             const leading_name = searchable
18616               .filter(a => leading(a.name().toLowerCase()))
18617               .sort(sortNames);
18618
18619             // matches value to preset suggestion name (original name is unhyphenated)
18620             const leading_suggestions = suggestions
18621               .filter(a => leadingStrict(a.originalName.toLowerCase()))
18622               .sort(sortNames);
18623
18624             // matches value to preset.terms values
18625             const leading_terms = searchable
18626               .filter(a => (a.terms() || []).some(leading));
18627
18628             // matches value to preset.tags values
18629             const leading_tag_values = searchable
18630               .filter(a => Object.values(a.tags || {}).filter(val => val !== '*').some(leading));
18631
18632             // finds close matches to value in preset.name
18633             const similar_name = searchable
18634               .map(a => ({ preset: a, dist: utilEditDistance(value, a.name()) }))
18635               .filter(a => a.dist + Math.min(value.length - a.preset.name().length, 0) < 3)
18636               .sort((a, b) => a.dist - b.dist)
18637               .map(a => a.preset);
18638
18639             // finds close matches to value to preset suggestion name (original name is unhyphenated)
18640             const similar_suggestions = suggestions
18641               .map(a => ({ preset: a, dist: utilEditDistance(value, a.originalName.toLowerCase()) }))
18642               .filter(a => a.dist + Math.min(value.length - a.preset.originalName.length, 0) < 1)
18643               .sort((a, b) => a.dist - b.dist)
18644               .map(a => a.preset);
18645
18646             // finds close matches to value in preset.terms
18647             const similar_terms = searchable
18648               .filter(a => {
18649                 return (a.terms() || []).some(b => {
18650                   return utilEditDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
18651                 });
18652               });
18653
18654             let results = leading_name.concat(
18655               leading_suggestions,
18656               leading_terms,
18657               leading_tag_values,
18658               similar_name,
18659               similar_suggestions,
18660               similar_terms
18661             ).slice(0, MAXRESULTS - 1);
18662
18663             if (geometry) {
18664               if (typeof geometry === 'string') {
18665                 results.push(_this.fallback(geometry));
18666               } else {
18667                 geometry.forEach(geom => results.push(_this.fallback(geom)));
18668               }
18669             }
18670
18671             return presetCollection(utilArrayUniq(results));
18672           };
18673
18674
18675           return _this;
18676         }
18677
18678         //
18679         // `presetCategory` builds a `presetCollection` of member presets,
18680         // decorated with some extra methods for searching and matching geometry
18681         //
18682         function presetCategory(categoryID, category, all) {
18683           let _this = Object.assign({}, category);   // shallow copy
18684
18685           _this.id = categoryID;
18686
18687           _this.members = presetCollection(
18688             category.members.map(presetID => all.item(presetID)).filter(Boolean)
18689           );
18690
18691           _this.geometry = _this.members.collection
18692             .reduce((acc, preset) => {
18693               for (let i in preset.geometry) {
18694                 const geometry = preset.geometry[i];
18695                 if (acc.indexOf(geometry) === -1) {
18696                   acc.push(geometry);
18697                 }
18698               }
18699               return acc;
18700             }, []);
18701
18702           _this.matchGeometry = (geom) => _this.geometry.indexOf(geom) >= 0;
18703
18704           _this.matchAllGeometry = (geometries) => _this.members.collection
18705             .some(preset => preset.matchAllGeometry(geometries));
18706
18707           _this.matchScore = () => -1;
18708
18709           _this.name = () => _t(`presets.categories.${categoryID}.name`, { 'default': categoryID });
18710
18711           _this.terms = () => [];
18712
18713
18714           return _this;
18715         }
18716
18717         //
18718         // `presetField` decorates a given `field` Object
18719         // with some extra methods for searching and matching geometry
18720         //
18721         function presetField(fieldID, field) {
18722           let _this = Object.assign({}, field);   // shallow copy
18723
18724           _this.id = fieldID;
18725
18726           // for use in classes, element ids, css selectors
18727           _this.safeid = utilSafeClassName(fieldID);
18728
18729           _this.matchGeometry = (geom) => !_this.geometry || _this.geometry.indexOf(geom) !== -1;
18730
18731           _this.matchAllGeometry = (geometries) => {
18732             return !_this.geometry || geometries.every(geom => _this.geometry.indexOf(geom) !== -1);
18733           };
18734
18735           _this.t = (scope, options) => _t(`presets.fields.${fieldID}.${scope}`, options);
18736
18737           _this.label = () => _this.overrideLabel || _this.t('label', { 'default': fieldID });
18738
18739           const _placeholder = _this.placeholder;
18740           _this.placeholder = () => _this.t('placeholder', { 'default': _placeholder });
18741
18742           _this.originalTerms = (_this.terms || []).join();
18743
18744           _this.terms = () => _this.t('terms', { 'default': _this.originalTerms })
18745             .toLowerCase().trim().split(/\s*,+\s*/);
18746
18747
18748           return _this;
18749         }
18750
18751         //
18752         // `presetPreset` decorates a given `preset` Object
18753         // with some extra methods for searching and matching geometry
18754         //
18755         function presetPreset(presetID, preset, addable, allFields, allPresets) {
18756           allFields = allFields || {};
18757           allPresets = allPresets || {};
18758           let _this = Object.assign({}, preset);   // shallow copy
18759           let _addable = addable || false;
18760           let _resolvedFields;      // cache
18761           let _resolvedMoreFields;  // cache
18762
18763           _this.id = presetID;
18764
18765           _this.safeid = utilSafeClassName(presetID);  // for use in css classes, selectors, element ids
18766
18767           _this.originalTerms = (_this.terms || []).join();
18768
18769           _this.originalName = _this.name || '';
18770
18771           _this.originalScore = _this.matchScore || 1;
18772
18773           _this.originalReference = _this.reference || {};
18774
18775           _this.originalFields = (_this.fields || []);
18776
18777           _this.originalMoreFields = (_this.moreFields || []);
18778
18779           _this.fields = () => _resolvedFields || (_resolvedFields = resolve('fields'));
18780
18781           _this.moreFields = () => _resolvedMoreFields || (_resolvedMoreFields = resolve('moreFields'));
18782
18783           _this.resetFields = () => _resolvedFields = _resolvedMoreFields = null;
18784
18785           _this.tags = _this.tags || {};
18786
18787           _this.addTags = _this.addTags || _this.tags;
18788
18789           _this.removeTags = _this.removeTags || _this.addTags;
18790
18791           _this.geometry = (_this.geometry || []);
18792
18793           _this.matchGeometry = (geom) => _this.geometry.indexOf(geom) >= 0;
18794
18795           _this.matchAllGeometry = (geoms) => geoms.every(_this.matchGeometry);
18796
18797           _this.matchScore = (entityTags) => {
18798             const tags = _this.tags;
18799             let seen = {};
18800             let score = 0;
18801
18802             // match on tags
18803             for (let k in tags) {
18804               seen[k] = true;
18805               if (entityTags[k] === tags[k]) {
18806                 score += _this.originalScore;
18807               } else if (tags[k] === '*' && k in entityTags) {
18808                 score += _this.originalScore / 2;
18809               } else {
18810                 return -1;
18811               }
18812             }
18813
18814             // boost score for additional matches in addTags - #6802
18815             const addTags = _this.addTags;
18816             for (let k in addTags) {
18817               if (!seen[k] && entityTags[k] === addTags[k]) {
18818                 score += _this.originalScore;
18819               }
18820             }
18821
18822             return score;
18823           };
18824
18825
18826           let _textCache = {};
18827           _this.t = (scope, options) => {
18828             const textID = `presets.presets.${presetID}.${scope}`;
18829             if (_textCache[textID]) return _textCache[textID];
18830             return _textCache[textID] = _t(textID, options);
18831           };
18832
18833
18834           _this.name = () => {
18835             if (_this.suggestion) {
18836               let path = presetID.split('/');
18837               path.pop();  // remove brand name
18838               // NOTE: insert an en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc)
18839               return _this.originalName + ' – ' + _t('presets.presets.' + path.join('/') + '.name');
18840             }
18841             return _this.t('name', { 'default': _this.originalName });
18842           };
18843
18844
18845           _this.terms = () => _this.t('terms', { 'default': _this.originalTerms })
18846             .toLowerCase().trim().split(/\s*,+\s*/);
18847
18848
18849           _this.isFallback = () => {
18850             const tagCount = Object.keys(_this.tags).length;
18851             return tagCount === 0 || (tagCount === 1 && _this.tags.hasOwnProperty('area'));
18852           };
18853
18854
18855           _this.addable = function(val) {
18856             if (!arguments.length) return _addable;
18857             _addable = val;
18858             return _this;
18859           };
18860
18861
18862           _this.reference = (geom) => {
18863             // Lookup documentation on Wikidata...
18864             const qid = _this.tags.wikidata || _this.tags['brand:wikidata'] || _this.tags['operator:wikidata'];
18865             if (qid) {
18866               return { qid: qid };
18867             }
18868
18869             // Lookup documentation on OSM Wikibase...
18870             let key = _this.originalReference.key || Object.keys(utilObjectOmit(_this.tags, 'name'))[0];
18871             let value = _this.originalReference.value || _this.tags[key];
18872
18873             if (geom === 'relation' && key === 'type') {
18874               if (value in _this.tags) {
18875                 key = value;
18876                 value = _this.tags[key];
18877               } else {
18878                 return { rtype: value };
18879               }
18880             }
18881
18882             if (value === '*') {
18883               return { key: key };
18884             } else {
18885               return { key: key, value: value };
18886             }
18887           };
18888
18889
18890           _this.unsetTags = (tags, geometry, skipFieldDefaults) => {
18891             tags = utilObjectOmit(tags, Object.keys(_this.removeTags));
18892
18893             if (geometry && !skipFieldDefaults) {
18894               _this.fields().forEach(field => {
18895                 if (field.matchGeometry(geometry) && field.key && field.default === tags[field.key]) {
18896                   delete tags[field.key];
18897                 }
18898               });
18899             }
18900
18901             delete tags.area;
18902             return tags;
18903           };
18904
18905
18906           _this.setTags = (tags, geometry, skipFieldDefaults) => {
18907             const addTags = _this.addTags;
18908             tags = Object.assign({}, tags);   // shallow copy
18909
18910             for (let k in addTags) {
18911               if (addTags[k] === '*') {
18912                 tags[k] = 'yes';
18913               } else {
18914                 tags[k] = addTags[k];
18915               }
18916             }
18917
18918             // Add area=yes if necessary.
18919             // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
18920             // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
18921             // 2. chosen preset doesn't have a key in osmAreaKeys (`railway=station`)
18922             if (!addTags.hasOwnProperty('area')) {
18923               delete tags.area;
18924               if (geometry === 'area') {
18925                 let needsAreaTag = true;
18926                 if (_this.geometry.indexOf('line') === -1) {
18927                   for (let k in addTags) {
18928                     if (k in osmAreaKeys) {
18929                       needsAreaTag = false;
18930                       break;
18931                     }
18932                   }
18933                 }
18934                 if (needsAreaTag) {
18935                   tags.area = 'yes';
18936                 }
18937               }
18938             }
18939
18940             if (geometry && !skipFieldDefaults) {
18941               _this.fields().forEach(field => {
18942                 if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) {
18943                   tags[field.key] = field.default;
18944                 }
18945               });
18946             }
18947
18948             return tags;
18949           };
18950
18951
18952           // For a preset without fields, use the fields of the parent preset.
18953           // Replace {preset} placeholders with the fields of the specified presets.
18954           function resolve(which) {
18955             const fieldIDs = (which === 'fields' ? _this.originalFields : _this.originalMoreFields);
18956             let resolved = [];
18957
18958             fieldIDs.forEach(fieldID => {
18959               const match = fieldID.match(/\{(.*)\}/);
18960               if (match !== null) {    // a presetID wrapped in braces {}
18961                 resolved = resolved.concat(inheritFields(match[1], which));
18962               } else if (allFields[fieldID]) {    // a normal fieldID
18963                 resolved.push(allFields[fieldID]);
18964               } else {
18965                 console.log(`Cannot resolve "${fieldID}" found in ${_this.id}.${which}`);  // eslint-disable-line no-console
18966               }
18967             });
18968
18969             // no fields resolved, so use the parent's if possible
18970             if (!resolved.length) {
18971               const endIndex = _this.id.lastIndexOf('/');
18972               const parentID = endIndex && _this.id.substring(0, endIndex);
18973               if (parentID) {
18974                 resolved = inheritFields(parentID, which);
18975               }
18976             }
18977
18978             return utilArrayUniq(resolved);
18979
18980
18981             // returns an array of fields to inherit from the given presetID, if found
18982             function inheritFields(presetID, which) {
18983               const parent = allPresets[presetID];
18984               if (!parent) return [];
18985
18986               if (which === 'fields') {
18987                 return parent.fields().filter(shouldInherit);
18988               } else if (which === 'moreFields') {
18989                 return parent.moreFields();
18990               } else {
18991                 return [];
18992               }
18993             }
18994
18995
18996             // Skip `fields` for the keys which define the preset.
18997             // These are usually `typeCombo` fields like `shop=*`
18998             function shouldInherit(f) {
18999               if (f.key && _this.tags[f.key] !== undefined &&
19000                 // inherit anyway if multiple values are allowed or just a checkbox
19001                 f.type !== 'multiCombo' && f.type !== 'semiCombo' && f.type !== 'check'
19002               ) return false;
19003
19004               return true;
19005             }
19006           }
19007
19008
19009           return _this;
19010         }
19011
19012         let _mainPresetIndex = presetIndex(); // singleton
19013
19014         //
19015         // `presetIndex` wraps a `presetCollection`
19016         // with methods for loading new data and returning defaults
19017         //
19018         function presetIndex() {
19019           const dispatch$1 = dispatch('favoritePreset', 'recentsChange');
19020           const MAXRECENTS = 30;
19021
19022           // seed the preset lists with geometry fallbacks
19023           const POINT = presetPreset('point', { name: 'Point', tags: {}, geometry: ['point', 'vertex'], matchScore: 0.1 } );
19024           const LINE = presetPreset('line', { name: 'Line', tags: {}, geometry: ['line'], matchScore: 0.1 } );
19025           const AREA = presetPreset('area', { name: 'Area', tags: { area: 'yes' }, geometry: ['area'], matchScore: 0.1 } );
19026           const RELATION = presetPreset('relation', { name: 'Relation', tags: {}, geometry: ['relation'], matchScore: 0.1 } );
19027
19028           let _this = presetCollection([POINT, LINE, AREA, RELATION]);
19029           let _presets = { point: POINT, line: LINE, area: AREA, relation: RELATION };
19030
19031           let _defaults = {
19032             point: presetCollection([POINT]),
19033             vertex: presetCollection([POINT]),
19034             line: presetCollection([LINE]),
19035             area: presetCollection([AREA]),
19036             relation: presetCollection([RELATION])
19037           };
19038
19039           let _fields = {};
19040           let _categories = {};
19041           let _universal = [];
19042           let _addablePresetIDs = null;   // Set of preset IDs that the user can add
19043           let _recents;
19044           let _favorites;
19045
19046           // Index of presets by (geometry, tag key).
19047           let _geometryIndex = { point: {}, vertex: {}, line: {}, area: {}, relation: {} };
19048
19049           let _loadPromise;
19050
19051           _this.ensureLoaded = () => {
19052             if (_loadPromise) return _loadPromise;
19053
19054             return _loadPromise = Promise.all([
19055                 _mainFileFetcher.get('preset_categories'),
19056                 _mainFileFetcher.get('preset_defaults'),
19057                 _mainFileFetcher.get('preset_presets'),
19058                 _mainFileFetcher.get('preset_fields')
19059               ])
19060               .then(vals => {
19061                 _this.merge({
19062                   categories: vals[0],
19063                   defaults: vals[1],
19064                   presets: vals[2],
19065                   fields: vals[3]
19066                 });
19067                 osmSetAreaKeys(_this.areaKeys());
19068                 osmSetPointTags(_this.pointTags());
19069                 osmSetVertexTags(_this.vertexTags());
19070               });
19071           };
19072
19073
19074           _this.merge = (d) => {
19075             // Merge Fields
19076             if (d.fields) {
19077               Object.keys(d.fields).forEach(fieldID => {
19078                 const f = d.fields[fieldID];
19079                 if (f) {   // add or replace
19080                   _fields[fieldID] = presetField(fieldID, f);
19081                 } else {   // remove
19082                   delete _fields[fieldID];
19083                 }
19084               });
19085             }
19086
19087             // Merge Presets
19088             if (d.presets) {
19089               Object.keys(d.presets).forEach(presetID => {
19090                 const p = d.presets[presetID];
19091                 if (p) {   // add or replace
19092                   const isAddable = !_addablePresetIDs || _addablePresetIDs.has(presetID);
19093                   _presets[presetID] = presetPreset(presetID, p, isAddable, _fields, _presets);
19094                 } else {   // remove (but not if it's a fallback)
19095                   const existing = _presets[presetID];
19096                   if (existing && !existing.isFallback()) {
19097                     delete _presets[presetID];
19098                   }
19099                 }
19100               });
19101             }
19102
19103             // Need to rebuild _this.collection before loading categories
19104             _this.collection = Object.values(_presets).concat(Object.values(_categories));
19105
19106             // Merge Categories
19107             if (d.categories) {
19108               Object.keys(d.categories).forEach(categoryID => {
19109                 const c = d.categories[categoryID];
19110                 if (c) {   // add or replace
19111                   _categories[categoryID] = presetCategory(categoryID, c, _this);
19112                 } else {   // remove
19113                   delete _categories[categoryID];
19114                 }
19115               });
19116             }
19117
19118             // Rebuild _this.collection after loading categories
19119             _this.collection = Object.values(_presets).concat(Object.values(_categories));
19120
19121             // Merge Defaults
19122             if (d.defaults) {
19123               Object.keys(d.defaults).forEach(geometry => {
19124                 const def = d.defaults[geometry];
19125                 if (Array.isArray(def)) {   // add or replace
19126                   _defaults[geometry] = presetCollection(
19127                     def.map(id => _presets[id] || _categories[id]).filter(Boolean)
19128                   );
19129                 } else {   // remove
19130                   delete _defaults[geometry];
19131                 }
19132               });
19133             }
19134
19135             // Rebuild universal fields array
19136             _universal = Object.values(_fields).filter(field => field.universal);
19137
19138             // Reset all the preset fields - they'll need to be resolved again
19139             Object.values(_presets).forEach(preset => preset.resetFields());
19140
19141             // Rebuild geometry index
19142             _geometryIndex = { point: {}, vertex: {}, line: {}, area: {}, relation: {} };
19143             _this.collection.forEach(preset => {
19144               (preset.geometry || []).forEach(geometry => {
19145                 let g = _geometryIndex[geometry];
19146                 for (let key in preset.tags) {
19147                   (g[key] = g[key] || []).push(preset);
19148                 }
19149               });
19150             });
19151
19152             return _this;
19153           };
19154
19155
19156           _this.match = (entity, resolver) => {
19157             return resolver.transient(entity, 'presetMatch', () => {
19158               let geometry = entity.geometry(resolver);
19159               // Treat entities on addr:interpolation lines as points, not vertices - #3241
19160               if (geometry === 'vertex' && entity.isOnAddressLine(resolver)) {
19161                 geometry = 'point';
19162               }
19163               return _this.matchTags(entity.tags, geometry);
19164             });
19165           };
19166
19167
19168           _this.matchTags = (tags, geometry) => {
19169             const geometryMatches = _geometryIndex[geometry];
19170             let address;
19171             let best = -1;
19172             let match;
19173
19174             for (let k in tags) {
19175               // If any part of an address is present, allow fallback to "Address" preset - #4353
19176               if (/^addr:/.test(k) && geometryMatches['addr:*']) {
19177                 address = geometryMatches['addr:*'][0];
19178               }
19179
19180               const keyMatches = geometryMatches[k];
19181               if (!keyMatches) continue;
19182
19183               for (let i = 0; i < keyMatches.length; i++) {
19184                 const score = keyMatches[i].matchScore(tags);
19185                 if (score > best) {
19186                   best = score;
19187                   match = keyMatches[i];
19188                 }
19189               }
19190             }
19191
19192             if (address && (!match || match.isFallback())) {
19193               match = address;
19194             }
19195             return match || _this.fallback(geometry);
19196           };
19197
19198
19199           _this.allowsVertex = (entity, resolver) => {
19200             if (entity.type !== 'node') return false;
19201             if (Object.keys(entity.tags).length === 0) return true;
19202
19203             return resolver.transient(entity, 'vertexMatch', () => {
19204               // address lines allow vertices to act as standalone points
19205               if (entity.isOnAddressLine(resolver)) return true;
19206
19207               const geometries = osmNodeGeometriesForTags(entity.tags);
19208               if (geometries.vertex) return true;
19209               if (geometries.point) return false;
19210               // allow vertices for unspecified points
19211               return true;
19212             });
19213           };
19214
19215
19216           // Because of the open nature of tagging, iD will never have a complete
19217           // list of tags used in OSM, so we want it to have logic like "assume
19218           // that a closed way with an amenity tag is an area, unless the amenity
19219           // is one of these specific types". This function computes a structure
19220           // that allows testing of such conditions, based on the presets designated
19221           // as as supporting (or not supporting) the area geometry.
19222           //
19223           // The returned object L is a keeplist/discardlist of tags. A closed way
19224           // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
19225           // (see `Way#isArea()`). In other words, the keys of L form the keeplist,
19226           // and the subkeys form the discardlist.
19227           _this.areaKeys = () => {
19228             // The ignore list is for keys that imply lines. (We always add `area=yes` for exceptions)
19229             const ignore = ['barrier', 'highway', 'footway', 'railway', 'junction', 'type'];
19230             let areaKeys = {};
19231
19232             // ignore name-suggestion-index and deprecated presets
19233             const presets = _this.collection.filter(p => !p.suggestion && !p.replacement);
19234
19235             // keeplist
19236             presets.forEach(p => {
19237               let key;
19238               for (key in p.tags) break;  // pick the first tag
19239               if (!key) return;
19240               if (ignore.indexOf(key) !== -1) return;
19241
19242               if (p.geometry.indexOf('area') !== -1) {    // probably an area..
19243                 areaKeys[key] = areaKeys[key] || {};
19244               }
19245             });
19246
19247             // discardlist
19248             presets.forEach(p => {
19249               let key;
19250               for (key in p.addTags) {
19251                 // examine all addTags to get a better sense of what can be tagged on lines - #6800
19252                 const value = p.addTags[key];
19253                 if (key in areaKeys &&                    // probably an area...
19254                   p.geometry.indexOf('line') !== -1 &&    // but sometimes a line
19255                   value !== '*') {
19256                   areaKeys[key][value] = true;
19257                 }
19258               }
19259             });
19260
19261             return areaKeys;
19262           };
19263
19264
19265           _this.pointTags = () => {
19266             return _this.collection.reduce((pointTags, d) => {
19267               // ignore name-suggestion-index, deprecated, and generic presets
19268               if (d.suggestion || d.replacement || d.searchable === false) return pointTags;
19269
19270               // only care about the primary tag
19271               let key;
19272               for (key in d.tags) break;  // pick the first tag
19273               if (!key) return pointTags;
19274
19275               // if this can be a point
19276               if (d.geometry.indexOf('point') !== -1) {
19277                 pointTags[key] = pointTags[key] || {};
19278                 pointTags[key][d.tags[key]] = true;
19279               }
19280               return pointTags;
19281             }, {});
19282           };
19283
19284
19285           _this.vertexTags = () => {
19286             return _this.collection.reduce((vertexTags, d) => {
19287               // ignore name-suggestion-index, deprecated, and generic presets
19288               if (d.suggestion || d.replacement || d.searchable === false) return vertexTags;
19289
19290               // only care about the primary tag
19291               let key;
19292               for (key in d.tags) break;   // pick the first tag
19293               if (!key) return vertexTags;
19294
19295               // if this can be a vertex
19296               if (d.geometry.indexOf('vertex') !== -1) {
19297                 vertexTags[key] = vertexTags[key] || {};
19298                 vertexTags[key][d.tags[key]] = true;
19299               }
19300               return vertexTags;
19301             }, {});
19302           };
19303
19304
19305           _this.field = (id) => _fields[id];
19306
19307           _this.universal = () => _universal;
19308
19309
19310           _this.defaults = (geometry, n, startWithRecents) => {
19311             let recents = [];
19312             if (startWithRecents) {
19313               recents = _this.recent().matchGeometry(geometry).collection.slice(0, 4);
19314             }
19315             let defaults;
19316             if (_addablePresetIDs) {
19317               defaults = Array.from(_addablePresetIDs).map(function(id) {
19318                 var preset = _this.item(id);
19319                 if (preset && preset.matchGeometry(geometry)) return preset;
19320                 return null;
19321               }).filter(Boolean);
19322             } else {
19323               defaults = _defaults[geometry].collection.concat(_this.fallback(geometry));
19324             }
19325
19326             return presetCollection(
19327               utilArrayUniq(recents.concat(defaults)).slice(0, n - 1)
19328             );
19329           };
19330
19331           // pass a Set of addable preset ids
19332           _this.addablePresetIDs = function(val) {
19333             if (!arguments.length) return _addablePresetIDs;
19334
19335             // accept and convert arrays
19336             if (Array.isArray(val)) val = new Set(val);
19337
19338             _addablePresetIDs = val;
19339             if (_addablePresetIDs) {   // reset all presets
19340               _this.collection.forEach(p => {
19341                 // categories aren't addable
19342                 if (p.addable) p.addable(_addablePresetIDs.has(p.id));
19343               });
19344             } else {
19345               _this.collection.forEach(p => {
19346                 if (p.addable) p.addable(true);
19347               });
19348             }
19349
19350             return _this;
19351           };
19352
19353
19354           _this.recent = () => {
19355             return presetCollection(
19356               utilArrayUniq(_this.getRecents().map(d => d.preset))
19357             );
19358           };
19359
19360
19361           function RibbonItem(preset, source) {
19362             let item = {};
19363             item.preset = preset;
19364             item.source = source;
19365
19366             item.isFavorite = () => item.source === 'favorite';
19367             item.isRecent = () => item.source === 'recent';
19368             item.matches = (preset) => item.preset.id === preset.id;
19369             item.minified = () => ({ pID: item.preset.id });
19370
19371             return item;
19372           }
19373
19374
19375           function ribbonItemForMinified(d, source) {
19376             if (d && d.pID) {
19377               const preset = _this.item(d.pID);
19378               if (!preset) return null;
19379               return RibbonItem(preset, source);
19380             }
19381             return null;
19382           }
19383
19384
19385           _this.getGenericRibbonItems = () => {
19386             return ['point', 'line', 'area'].map(id => RibbonItem(_this.item(id), 'generic'));
19387           };
19388
19389
19390           _this.getAddable = () => {
19391               if (!_addablePresetIDs) return [];
19392
19393               return _addablePresetIDs.map((id) => {
19394                 const preset = _this.item(id);
19395                 if (preset) {
19396                   return RibbonItem(preset, 'addable');
19397                 }
19398               }).filter(Boolean);
19399           };
19400
19401
19402           function setRecents(items) {
19403             _recents = items;
19404             const minifiedItems = items.map(d => d.minified());
19405             corePreferences('preset_recents', JSON.stringify(minifiedItems));
19406             dispatch$1.call('recentsChange');
19407           }
19408
19409
19410           _this.getRecents = () => {
19411             if (!_recents) {
19412               // fetch from local storage
19413               _recents = (JSON.parse(corePreferences('preset_recents')) || [])
19414                 .reduce((acc, d) => {
19415                   let item = ribbonItemForMinified(d, 'recent');
19416                   if (item && item.preset.addable()) acc.push(item);
19417                   return acc;
19418                 }, []);
19419             }
19420             return _recents;
19421           };
19422
19423
19424           _this.addRecent = (preset, besidePreset, after) => {
19425             const recents = _this.getRecents();
19426
19427             const beforeItem = _this.recentMatching(besidePreset);
19428             let toIndex = recents.indexOf(beforeItem);
19429             if (after) toIndex += 1;
19430
19431             const newItem = RibbonItem(preset, 'recent');
19432             recents.splice(toIndex, 0, newItem);
19433             setRecents(recents);
19434           };
19435
19436
19437           _this.removeRecent = (preset) => {
19438             const item = _this.recentMatching(preset);
19439             if (item) {
19440               let items = _this.getRecents();
19441               items.splice(items.indexOf(item), 1);
19442               setRecents(items);
19443             }
19444           };
19445
19446
19447           _this.recentMatching = (preset) => {
19448             const items = _this.getRecents();
19449             for (let i in items) {
19450               if (items[i].matches(preset)) {
19451                 return items[i];
19452               }
19453             }
19454             return null;
19455           };
19456
19457
19458           _this.moveItem = (items, fromIndex, toIndex) => {
19459             if (fromIndex === toIndex ||
19460               fromIndex < 0 || toIndex < 0 ||
19461               fromIndex >= items.length || toIndex >= items.length
19462             ) return null;
19463
19464             items.splice(toIndex, 0, items.splice(fromIndex, 1)[0]);
19465             return items;
19466           };
19467
19468
19469           _this.moveRecent = (item, beforeItem) => {
19470             const recents = _this.getRecents();
19471             const fromIndex = recents.indexOf(item);
19472             const toIndex = recents.indexOf(beforeItem);
19473             const items = _this.moveItem(recents, fromIndex, toIndex);
19474             if (items) setRecents(items);
19475           };
19476
19477
19478           _this.setMostRecent = (preset) => {
19479             if (preset.searchable === false) return;
19480
19481             let items = _this.getRecents();
19482             let item = _this.recentMatching(preset);
19483             if (item) {
19484               items.splice(items.indexOf(item), 1);
19485             } else {
19486               item = RibbonItem(preset, 'recent');
19487             }
19488
19489             // remove the last recent (first in, first out)
19490             while (items.length >= MAXRECENTS) {
19491               items.pop();
19492             }
19493
19494             // prepend array
19495             items.unshift(item);
19496             setRecents(items);
19497           };
19498
19499           function setFavorites(items) {
19500             _favorites = items;
19501             const minifiedItems = items.map(d => d.minified());
19502             corePreferences('preset_favorites', JSON.stringify(minifiedItems));
19503
19504             // call update
19505             dispatch$1.call('favoritePreset');
19506           }
19507
19508           _this.addFavorite = (preset, besidePreset, after) => {
19509               const favorites = _this.getFavorites();
19510
19511               const beforeItem = _this.favoriteMatching(besidePreset);
19512               let toIndex = favorites.indexOf(beforeItem);
19513               if (after) toIndex += 1;
19514
19515               const newItem = RibbonItem(preset, 'favorite');
19516               favorites.splice(toIndex, 0, newItem);
19517               setFavorites(favorites);
19518           };
19519
19520           _this.toggleFavorite = (preset) => {
19521             const favs = _this.getFavorites();
19522             const favorite = _this.favoriteMatching(preset);
19523             if (favorite) {
19524               favs.splice(favs.indexOf(favorite), 1);
19525             } else {
19526               // only allow 10 favorites
19527               if (favs.length === 10) {
19528                   // remove the last favorite (last in, first out)
19529                   favs.pop();
19530               }
19531               // append array
19532               favs.push(RibbonItem(preset, 'favorite'));
19533             }
19534             setFavorites(favs);
19535           };
19536
19537
19538           _this.removeFavorite = (preset) => {
19539             const item = _this.favoriteMatching(preset);
19540             if (item) {
19541               const items = _this.getFavorites();
19542               items.splice(items.indexOf(item), 1);
19543               setFavorites(items);
19544             }
19545           };
19546
19547
19548           _this.getFavorites = () => {
19549             if (!_favorites) {
19550
19551               // fetch from local storage
19552               let rawFavorites = JSON.parse(corePreferences('preset_favorites'));
19553
19554               if (!rawFavorites) {
19555                 rawFavorites = [];
19556                 corePreferences('preset_favorites', JSON.stringify(rawFavorites));
19557               }
19558
19559               _favorites = rawFavorites.reduce((output, d) => {
19560                 const item = ribbonItemForMinified(d, 'favorite');
19561                 if (item && item.preset.addable()) output.push(item);
19562                 return output;
19563               }, []);
19564             }
19565             return _favorites;
19566           };
19567
19568
19569           _this.favoriteMatching = (preset) => {
19570             const favs = _this.getFavorites();
19571             for (let index in favs) {
19572               if (favs[index].matches(preset)) {
19573                 return favs[index];
19574               }
19575             }
19576             return null;
19577           };
19578
19579
19580           return utilRebind(_this, dispatch$1, 'on');
19581         }
19582
19583         function utilTagText(entity) {
19584             var obj = (entity && entity.tags) || {};
19585             return Object.keys(obj)
19586                 .map(function(k) { return k + '=' + obj[k]; })
19587                 .join(', ');
19588         }
19589
19590
19591         function utilTotalExtent(array, graph) {
19592             var extent = geoExtent();
19593             var val, entity;
19594             for (var i = 0; i < array.length; i++) {
19595                 val = array[i];
19596                 entity = typeof val === 'string' ? graph.hasEntity(val) : val;
19597                 if (entity) {
19598                     extent._extend(entity.extent(graph));
19599                 }
19600             }
19601             return extent;
19602         }
19603
19604
19605         function utilTagDiff(oldTags, newTags) {
19606             var tagDiff = [];
19607             var keys = utilArrayUnion(Object.keys(oldTags), Object.keys(newTags)).sort();
19608             keys.forEach(function(k) {
19609                 var oldVal = oldTags[k];
19610                 var newVal = newTags[k];
19611
19612                 if ((oldVal || oldVal === '') && (newVal === undefined || newVal !== oldVal)) {
19613                     tagDiff.push({
19614                         type: '-',
19615                         key: k,
19616                         oldVal: oldVal,
19617                         newVal: newVal,
19618                         display: '- ' + k + '=' + oldVal
19619                     });
19620                 }
19621                 if ((newVal || newVal === '') && (oldVal === undefined || newVal !== oldVal)) {
19622                     tagDiff.push({
19623                         type: '+',
19624                         key: k,
19625                         oldVal: oldVal,
19626                         newVal: newVal,
19627                         display: '+ ' + k + '=' + newVal
19628                     });
19629                 }
19630             });
19631             return tagDiff;
19632         }
19633
19634
19635         function utilEntitySelector(ids) {
19636             return ids.length ? '.' + ids.join(',.') : 'nothing';
19637         }
19638
19639
19640         // returns an selector to select entity ids for:
19641         //  - entityIDs passed in
19642         //  - shallow descendant entityIDs for any of those entities that are relations
19643         function utilEntityOrMemberSelector(ids, graph) {
19644             var seen = new Set(ids);
19645             ids.forEach(collectShallowDescendants);
19646             return utilEntitySelector(Array.from(seen));
19647
19648             function collectShallowDescendants(id) {
19649                 var entity = graph.hasEntity(id);
19650                 if (!entity || entity.type !== 'relation') return;
19651
19652                 entity.members
19653                     .map(function(member) { return member.id; })
19654                     .forEach(function(id) { seen.add(id); });
19655             }
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 utilEntityOrDeepMemberSelector(ids, graph) {
19663             return utilEntitySelector(utilEntityAndDeepMemberIDs(ids, graph));
19664         }
19665
19666
19667         // returns an selector to select entity ids for:
19668         //  - entityIDs passed in
19669         //  - deep descendant entityIDs for any of those entities that are relations
19670         function utilEntityAndDeepMemberIDs(ids, graph) {
19671             var seen = new Set();
19672             ids.forEach(collectDeepDescendants);
19673             return Array.from(seen);
19674
19675             function collectDeepDescendants(id) {
19676                 if (seen.has(id)) return;
19677                 seen.add(id);
19678
19679                 var entity = graph.hasEntity(id);
19680                 if (!entity || entity.type !== 'relation') return;
19681
19682                 entity.members
19683                     .map(function(member) { return member.id; })
19684                     .forEach(collectDeepDescendants);   // recurse
19685             }
19686         }
19687
19688         // returns an selector to select entity ids for:
19689         //  - deep descendant entityIDs for any of those entities that are relations
19690         function utilDeepMemberSelector(ids, graph, skipMultipolgonMembers) {
19691             var idsSet = new Set(ids);
19692             var seen = new Set();
19693             var returners = new Set();
19694             ids.forEach(collectDeepDescendants);
19695             return utilEntitySelector(Array.from(returners));
19696
19697             function collectDeepDescendants(id) {
19698                 if (seen.has(id)) return;
19699                 seen.add(id);
19700
19701                 if (!idsSet.has(id)) {
19702                     returners.add(id);
19703                 }
19704
19705                 var entity = graph.hasEntity(id);
19706                 if (!entity || entity.type !== 'relation') return;
19707                 if (skipMultipolgonMembers && entity.isMultipolygon()) return;
19708                 entity.members
19709                     .map(function(member) { return member.id; })
19710                     .forEach(collectDeepDescendants);   // recurse
19711             }
19712         }
19713
19714
19715         // Adds or removes highlight styling for the specified entities
19716         function utilHighlightEntities(ids, highlighted, context) {
19717             context.surface()
19718                 .selectAll(utilEntityOrDeepMemberSelector(ids, context.graph()))
19719                 .classed('highlighted', highlighted);
19720         }
19721
19722
19723         // returns an Array that is the union of:
19724         //  - nodes for any nodeIDs passed in
19725         //  - child nodes of any wayIDs passed in
19726         //  - descendant member and child nodes of relationIDs passed in
19727         function utilGetAllNodes(ids, graph) {
19728             var seen = new Set();
19729             var nodes = new Set();
19730
19731             ids.forEach(collectNodes);
19732             return Array.from(nodes);
19733
19734             function collectNodes(id) {
19735                 if (seen.has(id)) return;
19736                 seen.add(id);
19737
19738                 var entity = graph.hasEntity(id);
19739                 if (!entity) return;
19740
19741                 if (entity.type === 'node') {
19742                     nodes.add(entity);
19743                 } else if (entity.type === 'way') {
19744                     entity.nodes.forEach(collectNodes);
19745                 } else {
19746                     entity.members
19747                         .map(function(member) { return member.id; })
19748                         .forEach(collectNodes);   // recurse
19749                 }
19750             }
19751         }
19752
19753
19754         function utilDisplayName(entity) {
19755             var localizedNameKey = 'name:' + _mainLocalizer.languageCode().toLowerCase();
19756             var name = entity.tags[localizedNameKey] || entity.tags.name || '';
19757             var network = entity.tags.cycle_network || entity.tags.network;
19758
19759             if (!name && entity.tags.ref) {
19760                 name = entity.tags.ref;
19761                 if (network) {
19762                     name = network + ' ' + name;
19763                 }
19764             }
19765
19766             return name;
19767         }
19768
19769
19770         function utilDisplayNameForPath(entity) {
19771             var name = utilDisplayName(entity);
19772             var isFirefox = utilDetect().browser.toLowerCase().indexOf('firefox') > -1;
19773
19774             if (!isFirefox && name && rtlRegex.test(name)) {
19775                 name = fixRTLTextForSvg(name);
19776             }
19777
19778             return name;
19779         }
19780
19781
19782         function utilDisplayType(id) {
19783             return {
19784                 n: _t('inspector.node'),
19785                 w: _t('inspector.way'),
19786                 r: _t('inspector.relation')
19787             }[id.charAt(0)];
19788         }
19789
19790
19791         function utilDisplayLabel(entity, graph) {
19792             var displayName = utilDisplayName(entity);
19793             if (displayName) {
19794                 // use the display name if there is one
19795                 return displayName;
19796             }
19797             var preset = _mainPresetIndex.match(entity, graph);
19798             if (preset && preset.name()) {
19799                 // use the preset name if there is a match
19800                 return preset.name();
19801             }
19802             // fallback to the display type (node/way/relation)
19803             return utilDisplayType(entity.id);
19804         }
19805
19806
19807         function utilEntityRoot(entityType) {
19808             return {
19809                 node: 'n',
19810                 way: 'w',
19811                 relation: 'r'
19812             }[entityType];
19813         }
19814
19815
19816         // Returns a single object containing the tags of all the given entities.
19817         // Example:
19818         // {
19819         //   highway: 'service',
19820         //   service: 'parking_aisle'
19821         // }
19822         //           +
19823         // {
19824         //   highway: 'service',
19825         //   service: 'driveway',
19826         //   width: '3'
19827         // }
19828         //           =
19829         // {
19830         //   highway: 'service',
19831         //   service: [ 'driveway', 'parking_aisle' ],
19832         //   width: [ '3', undefined ]
19833         // }
19834         function utilCombinedTags(entityIDs, graph) {
19835
19836             var tags = {};
19837             var tagCounts = {};
19838             var allKeys = new Set();
19839
19840             var entities = entityIDs.map(function(entityID) {
19841                 return graph.hasEntity(entityID);
19842             }).filter(Boolean);
19843
19844             // gather the aggregate keys
19845             entities.forEach(function(entity) {
19846                 var keys = Object.keys(entity.tags).filter(Boolean);
19847                 keys.forEach(function(key) {
19848                     allKeys.add(key);
19849                 });
19850             });
19851
19852             entities.forEach(function(entity) {
19853
19854                 allKeys.forEach(function(key) {
19855
19856                     var value = entity.tags[key]; // purposely allow `undefined`
19857
19858                     if (!tags.hasOwnProperty(key)) {
19859                         // first value, set as raw
19860                         tags[key] = value;
19861                     } else {
19862                         if (!Array.isArray(tags[key])) {
19863                             if (tags[key] !== value) {
19864                                 // first alternate value, replace single value with array
19865                                 tags[key] = [tags[key], value];
19866                             }
19867                         } else { // type is array
19868                             if (tags[key].indexOf(value) === -1) {
19869                                 // subsequent alternate value, add to array
19870                                 tags[key].push(value);
19871                             }
19872                         }
19873                     }
19874
19875                     var tagHash = key + '=' + value;
19876                     if (!tagCounts[tagHash]) tagCounts[tagHash] = 0;
19877                     tagCounts[tagHash] += 1;
19878                 });
19879             });
19880
19881             for (var key in tags) {
19882                 if (!Array.isArray(tags[key])) continue;
19883
19884                 // sort values by frequency then alphabetically
19885                 tags[key] = tags[key].sort(function(val1, val2) {
19886                     var key = key; // capture
19887                     var count2 = tagCounts[key + '=' + val2];
19888                     var count1 = tagCounts[key + '=' + val1];
19889                     if (count2 !== count1) {
19890                         return count2 - count1;
19891                     }
19892                     if (val2 && val1) {
19893                         return val1.localeCompare(val2);
19894                     }
19895                     return val1 ? 1 : -1;
19896                 });
19897             }
19898
19899             return tags;
19900         }
19901
19902
19903         function utilStringQs(str) {
19904             var i = 0;  // advance past any leading '?' or '#' characters
19905             while (i < str.length && (str[i] === '?' || str[i] === '#')) i++;
19906             str = str.slice(i);
19907
19908             return str.split('&').reduce(function(obj, pair){
19909                 var parts = pair.split('=');
19910                 if (parts.length === 2) {
19911                     obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]);
19912                 }
19913                 return obj;
19914             }, {});
19915         }
19916
19917
19918         function utilQsString(obj, noencode) {
19919             // encode everything except special characters used in certain hash parameters:
19920             // "/" in map states, ":", ",", {" and "}" in background
19921             function softEncode(s) {
19922                 return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
19923             }
19924
19925             return Object.keys(obj).sort().map(function(key) {
19926                 return encodeURIComponent(key) + '=' + (
19927                     noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
19928             }).join('&');
19929         }
19930
19931
19932         function utilPrefixDOMProperty(property) {
19933             var prefixes = ['webkit', 'ms', 'moz', 'o'];
19934             var i = -1;
19935             var n = prefixes.length;
19936             var s = document.body;
19937
19938             if (property in s)
19939                 return property;
19940
19941             property = property.substr(0, 1).toUpperCase() + property.substr(1);
19942
19943             while (++i < n) {
19944                 if (prefixes[i] + property in s) {
19945                     return prefixes[i] + property;
19946                 }
19947             }
19948
19949             return false;
19950         }
19951
19952
19953         function utilPrefixCSSProperty(property) {
19954             var prefixes = ['webkit', 'ms', 'Moz', 'O'];
19955             var i = -1;
19956             var n = prefixes.length;
19957             var s = document.body.style;
19958
19959             if (property.toLowerCase() in s) {
19960                 return property.toLowerCase();
19961             }
19962
19963             while (++i < n) {
19964                 if (prefixes[i] + property in s) {
19965                     return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
19966                 }
19967             }
19968
19969             return false;
19970         }
19971
19972
19973         var transformProperty;
19974         function utilSetTransform(el, x, y, scale) {
19975             var prop = transformProperty = transformProperty || utilPrefixCSSProperty('Transform');
19976             var translate = utilDetect().opera ? 'translate('   + x + 'px,' + y + 'px)'
19977                 : 'translate3d(' + x + 'px,' + y + 'px,0)';
19978             return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
19979         }
19980
19981
19982         // Calculates Levenshtein distance between two strings
19983         // see:  https://en.wikipedia.org/wiki/Levenshtein_distance
19984         // first converts the strings to lowercase and replaces diacritic marks with ascii equivalents.
19985         function utilEditDistance(a, b) {
19986             a = remove$1(a.toLowerCase());
19987             b = remove$1(b.toLowerCase());
19988             if (a.length === 0) return b.length;
19989             if (b.length === 0) return a.length;
19990             var matrix = [];
19991             for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; }
19992             for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; }
19993             for (i = 1; i <= b.length; i++) {
19994                 for (j = 1; j <= a.length; j++) {
19995                     if (b.charAt(i-1) === a.charAt(j-1)) {
19996                         matrix[i][j] = matrix[i-1][j-1];
19997                     } else {
19998                         matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
19999                             Math.min(matrix[i][j-1] + 1, // insertion
20000                             matrix[i-1][j] + 1)); // deletion
20001                     }
20002                 }
20003             }
20004             return matrix[b.length][a.length];
20005         }
20006
20007
20008         // a d3.mouse-alike which
20009         // 1. Only works on HTML elements, not SVG
20010         // 2. Does not cause style recalculation
20011         function utilFastMouse(container) {
20012             var rect = container.getBoundingClientRect();
20013             var rectLeft = rect.left;
20014             var rectTop = rect.top;
20015             var clientLeft = +container.clientLeft;
20016             var clientTop = +container.clientTop;
20017             return function(e) {
20018                 return [
20019                     e.clientX - rectLeft - clientLeft,
20020                     e.clientY - rectTop - clientTop];
20021             };
20022         }
20023
20024
20025         function utilAsyncMap(inputs, func, callback) {
20026             var remaining = inputs.length;
20027             var results = [];
20028             var errors = [];
20029
20030             inputs.forEach(function(d, i) {
20031                 func(d, function done(err, data) {
20032                     errors[i] = err;
20033                     results[i] = data;
20034                     remaining--;
20035                     if (!remaining) callback(errors, results);
20036                 });
20037             });
20038         }
20039
20040
20041         // wraps an index to an interval [0..length-1]
20042         function utilWrap(index, length) {
20043             if (index < 0) {
20044                 index += Math.ceil(-index/length)*length;
20045             }
20046             return index % length;
20047         }
20048
20049
20050         /**
20051          * a replacement for functor
20052          *
20053          * @param {*} value any value
20054          * @returns {Function} a function that returns that value or the value if it's a function
20055          */
20056         function utilFunctor(value) {
20057             if (typeof value === 'function') return value;
20058             return function() {
20059                 return value;
20060             };
20061         }
20062
20063
20064         function utilNoAuto(selection) {
20065             var isText = (selection.size() && selection.node().tagName.toLowerCase() === 'textarea');
20066
20067             return selection
20068                 // assign 'new-password' even for non-password fields to prevent browsers (Chrome) ignoring 'off'
20069                 .attr('autocomplete', 'new-password')
20070                 .attr('autocorrect', 'off')
20071                 .attr('autocapitalize', 'off')
20072                 .attr('spellcheck', isText ? 'true' : 'false');
20073         }
20074
20075
20076         // https://stackoverflow.com/questions/194846/is-there-any-kind-of-hash-code-function-in-javascript
20077         // https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
20078         function utilHashcode(str) {
20079             var hash = 0;
20080             if (str.length === 0) {
20081                 return hash;
20082             }
20083             for (var i = 0; i < str.length; i++) {
20084                 var char = str.charCodeAt(i);
20085                 hash = ((hash << 5) - hash) + char;
20086                 hash = hash & hash; // Convert to 32bit integer
20087             }
20088             return hash;
20089         }
20090
20091         // Returns version of `str` with all runs of special characters replaced by `_`;
20092         // suitable for HTML ids, classes, selectors, etc.
20093         function utilSafeClassName(str) {
20094             return str.toLowerCase().replace(/[^a-z0-9]+/g, '_');
20095         }
20096
20097         // Returns string based on `val` that is highly unlikely to collide with an id
20098         // used previously or that's present elsewhere in the document. Useful for preventing
20099         // browser-provided autofills or when embedding iD on pages with unknown elements.
20100         function utilUniqueDomId(val) {
20101             return 'ideditor-' + utilSafeClassName(val.toString()) + '-' + new Date().getTime().toString();
20102         }
20103
20104         // Returns the length of `str` in unicode characters. This can be less than
20105         // `String.length()` since a single unicode character can be composed of multiple
20106         // JavaScript UTF-16 code units.
20107         function utilUnicodeCharsCount(str) {
20108             // Native ES2015 implementations of `Array.from` split strings into unicode characters
20109             return Array.from(str).length;
20110         }
20111
20112         // Returns a new string representing `str` cut from its start to `limit` length
20113         // in unicode characters. Note that this runs the risk of splitting graphemes.
20114         function utilUnicodeCharsTruncated(str, limit) {
20115             return Array.from(str).slice(0, limit).join('');
20116         }
20117
20118         function osmEntity(attrs) {
20119             // For prototypal inheritance.
20120             if (this instanceof osmEntity) return;
20121
20122             // Create the appropriate subtype.
20123             if (attrs && attrs.type) {
20124                 return osmEntity[attrs.type].apply(this, arguments);
20125             } else if (attrs && attrs.id) {
20126                 return osmEntity[osmEntity.id.type(attrs.id)].apply(this, arguments);
20127             }
20128
20129             // Initialize a generic Entity (used only in tests).
20130             return (new osmEntity()).initialize(arguments);
20131         }
20132
20133
20134         osmEntity.id = function(type) {
20135             return osmEntity.id.fromOSM(type, osmEntity.id.next[type]--);
20136         };
20137
20138
20139         osmEntity.id.next = {
20140             changeset: -1, node: -1, way: -1, relation: -1
20141         };
20142
20143
20144         osmEntity.id.fromOSM = function(type, id) {
20145             return type[0] + id;
20146         };
20147
20148
20149         osmEntity.id.toOSM = function(id) {
20150             return id.slice(1);
20151         };
20152
20153
20154         osmEntity.id.type = function(id) {
20155             return { 'c': 'changeset', 'n': 'node', 'w': 'way', 'r': 'relation' }[id[0]];
20156         };
20157
20158
20159         // A function suitable for use as the second argument to d3.selection#data().
20160         osmEntity.key = function(entity) {
20161             return entity.id + 'v' + (entity.v || 0);
20162         };
20163
20164         var _deprecatedTagValuesByKey;
20165
20166         osmEntity.deprecatedTagValuesByKey = function(dataDeprecated) {
20167             if (!_deprecatedTagValuesByKey) {
20168                 _deprecatedTagValuesByKey = {};
20169                 dataDeprecated.forEach(function(d) {
20170                     var oldKeys = Object.keys(d.old);
20171                     if (oldKeys.length === 1) {
20172                         var oldKey = oldKeys[0];
20173                         var oldValue = d.old[oldKey];
20174                         if (oldValue !== '*') {
20175                             if (!_deprecatedTagValuesByKey[oldKey]) {
20176                                 _deprecatedTagValuesByKey[oldKey] = [oldValue];
20177                             } else {
20178                                 _deprecatedTagValuesByKey[oldKey].push(oldValue);
20179                             }
20180                         }
20181                     }
20182                 });
20183             }
20184             return _deprecatedTagValuesByKey;
20185         };
20186
20187
20188         osmEntity.prototype = {
20189
20190             tags: {},
20191
20192
20193             initialize: function(sources) {
20194                 for (var i = 0; i < sources.length; ++i) {
20195                     var source = sources[i];
20196                     for (var prop in source) {
20197                         if (Object.prototype.hasOwnProperty.call(source, prop)) {
20198                             if (source[prop] === undefined) {
20199                                 delete this[prop];
20200                             } else {
20201                                 this[prop] = source[prop];
20202                             }
20203                         }
20204                     }
20205                 }
20206
20207                 if (!this.id && this.type) {
20208                     this.id = osmEntity.id(this.type);
20209                 }
20210                 if (!this.hasOwnProperty('visible')) {
20211                     this.visible = true;
20212                 }
20213
20214                 return this;
20215             },
20216
20217
20218             copy: function(resolver, copies) {
20219                 if (copies[this.id])
20220                     return copies[this.id];
20221
20222                 var copy = osmEntity(this, { id: undefined, user: undefined, version: undefined });
20223                 copies[this.id] = copy;
20224
20225                 return copy;
20226             },
20227
20228
20229             osmId: function() {
20230                 return osmEntity.id.toOSM(this.id);
20231             },
20232
20233
20234             isNew: function() {
20235                 return this.osmId() < 0;
20236             },
20237
20238
20239             update: function(attrs) {
20240                 return osmEntity(this, attrs, { v: 1 + (this.v || 0) });
20241             },
20242
20243
20244             mergeTags: function(tags) {
20245                 var merged = Object.assign({}, this.tags);   // shallow copy
20246                 var changed = false;
20247                 for (var k in tags) {
20248                     var t1 = merged[k];
20249                     var t2 = tags[k];
20250                     if (!t1) {
20251                         changed = true;
20252                         merged[k] = t2;
20253                     } else if (t1 !== t2) {
20254                         changed = true;
20255                         merged[k] = utilUnicodeCharsTruncated(
20256                             utilArrayUnion(t1.split(/;\s*/), t2.split(/;\s*/)).join(';'),
20257                             255 // avoid exceeding character limit; see also services/osm.js -> maxCharsForTagValue()
20258                         );
20259                     }
20260                 }
20261                 return changed ? this.update({ tags: merged }) : this;
20262             },
20263
20264
20265             intersects: function(extent, resolver) {
20266                 return this.extent(resolver).intersects(extent);
20267             },
20268
20269
20270             hasNonGeometryTags: function() {
20271                 return Object.keys(this.tags).some(function(k) { return k !== 'area'; });
20272             },
20273
20274             hasParentRelations: function(resolver) {
20275                 return resolver.parentRelations(this).length > 0;
20276             },
20277
20278             hasInterestingTags: function() {
20279                 return Object.keys(this.tags).some(osmIsInterestingTag);
20280             },
20281
20282             hasWikidata: function() {
20283                 return !!this.tags.wikidata || !!this.tags['brand:wikidata'];
20284             },
20285
20286             isHighwayIntersection: function() {
20287                 return false;
20288             },
20289
20290             isDegenerate: function() {
20291                 return true;
20292             },
20293
20294             deprecatedTags: function(dataDeprecated) {
20295                 var tags = this.tags;
20296
20297                 // if there are no tags, none can be deprecated
20298                 if (Object.keys(tags).length === 0) return [];
20299
20300                 var deprecated = [];
20301                 dataDeprecated.forEach(function(d) {
20302                     var oldKeys = Object.keys(d.old);
20303                     var matchesDeprecatedTags = oldKeys.every(function(oldKey) {
20304                         if (!tags[oldKey]) return false;
20305                         if (d.old[oldKey] === '*') return true;
20306
20307                         var vals = tags[oldKey].split(';').filter(Boolean);
20308                         if (vals.length === 0) {
20309                             return false;
20310                         } else if (vals.length > 1) {
20311                             return vals.indexOf(d.old[oldKey]) !== -1;
20312                         } else {
20313                             if (tags[oldKey] === d.old[oldKey]) {
20314                                 if (d.replace && d.old[oldKey] === d.replace[oldKey]) {
20315                                     var replaceKeys = Object.keys(d.replace);
20316                                     return !replaceKeys.every(function(replaceKey) {
20317                                         return tags[replaceKey] === d.replace[replaceKey];
20318                                     });
20319                                 } else {
20320                                     return true;
20321                                 }
20322                             }
20323                         }
20324                         return false;
20325                     });
20326                     if (matchesDeprecatedTags) {
20327                         deprecated.push(d);
20328                     }
20329                 });
20330
20331                 return deprecated;
20332             }
20333         };
20334
20335         function osmLanes(entity) {
20336             if (entity.type !== 'way') return null;
20337             if (!entity.tags.highway) return null;
20338
20339             var tags = entity.tags;
20340             var isOneWay = entity.isOneWay();
20341             var laneCount = getLaneCount(tags, isOneWay);
20342             var maxspeed = parseMaxspeed(tags);
20343
20344             var laneDirections = parseLaneDirections(tags, isOneWay, laneCount);
20345             var forward = laneDirections.forward;
20346             var backward = laneDirections.backward;
20347             var bothways = laneDirections.bothways;
20348
20349             // parse the piped string 'x|y|z' format
20350             var turnLanes = {};
20351             turnLanes.unspecified = parseTurnLanes(tags['turn:lanes']);
20352             turnLanes.forward = parseTurnLanes(tags['turn:lanes:forward']);
20353             turnLanes.backward = parseTurnLanes(tags['turn:lanes:backward']);
20354
20355             var maxspeedLanes = {};
20356             maxspeedLanes.unspecified = parseMaxspeedLanes(tags['maxspeed:lanes'], maxspeed);
20357             maxspeedLanes.forward = parseMaxspeedLanes(tags['maxspeed:lanes:forward'], maxspeed);
20358             maxspeedLanes.backward = parseMaxspeedLanes(tags['maxspeed:lanes:backward'], maxspeed);
20359
20360             var psvLanes = {};
20361             psvLanes.unspecified = parseMiscLanes(tags['psv:lanes']);
20362             psvLanes.forward = parseMiscLanes(tags['psv:lanes:forward']);
20363             psvLanes.backward = parseMiscLanes(tags['psv:lanes:backward']);
20364
20365             var busLanes = {};
20366             busLanes.unspecified = parseMiscLanes(tags['bus:lanes']);
20367             busLanes.forward = parseMiscLanes(tags['bus:lanes:forward']);
20368             busLanes.backward = parseMiscLanes(tags['bus:lanes:backward']);
20369
20370             var taxiLanes = {};
20371             taxiLanes.unspecified = parseMiscLanes(tags['taxi:lanes']);
20372             taxiLanes.forward = parseMiscLanes(tags['taxi:lanes:forward']);
20373             taxiLanes.backward = parseMiscLanes(tags['taxi:lanes:backward']);
20374
20375             var hovLanes = {};
20376             hovLanes.unspecified = parseMiscLanes(tags['hov:lanes']);
20377             hovLanes.forward = parseMiscLanes(tags['hov:lanes:forward']);
20378             hovLanes.backward = parseMiscLanes(tags['hov:lanes:backward']);
20379
20380             var hgvLanes = {};
20381             hgvLanes.unspecified = parseMiscLanes(tags['hgv:lanes']);
20382             hgvLanes.forward = parseMiscLanes(tags['hgv:lanes:forward']);
20383             hgvLanes.backward = parseMiscLanes(tags['hgv:lanes:backward']);
20384
20385             var bicyclewayLanes = {};
20386             bicyclewayLanes.unspecified = parseBicycleWay(tags['bicycleway:lanes']);
20387             bicyclewayLanes.forward = parseBicycleWay(tags['bicycleway:lanes:forward']);
20388             bicyclewayLanes.backward = parseBicycleWay(tags['bicycleway:lanes:backward']);
20389
20390             var lanesObj = {
20391                 forward: [],
20392                 backward: [],
20393                 unspecified: []
20394             };
20395
20396             // map forward/backward/unspecified of each lane type to lanesObj
20397             mapToLanesObj(lanesObj, turnLanes, 'turnLane');
20398             mapToLanesObj(lanesObj, maxspeedLanes, 'maxspeed');
20399             mapToLanesObj(lanesObj, psvLanes, 'psv');
20400             mapToLanesObj(lanesObj, busLanes, 'bus');
20401             mapToLanesObj(lanesObj, taxiLanes, 'taxi');
20402             mapToLanesObj(lanesObj, hovLanes, 'hov');
20403             mapToLanesObj(lanesObj, hgvLanes, 'hgv');
20404             mapToLanesObj(lanesObj, bicyclewayLanes, 'bicycleway');
20405
20406             return {
20407                 metadata: {
20408                     count: laneCount,
20409                     oneway: isOneWay,
20410                     forward: forward,
20411                     backward: backward,
20412                     bothways: bothways,
20413                     turnLanes: turnLanes,
20414                     maxspeed: maxspeed,
20415                     maxspeedLanes: maxspeedLanes,
20416                     psvLanes: psvLanes,
20417                     busLanes: busLanes,
20418                     taxiLanes: taxiLanes,
20419                     hovLanes: hovLanes,
20420                     hgvLanes: hgvLanes,
20421                     bicyclewayLanes: bicyclewayLanes
20422                 },
20423                 lanes: lanesObj
20424             };
20425         }
20426
20427
20428         function getLaneCount(tags, isOneWay) {
20429             var count;
20430             if (tags.lanes) {
20431                 count = parseInt(tags.lanes, 10);
20432                 if (count > 0) {
20433                     return count;
20434                 }
20435             }
20436
20437
20438             switch (tags.highway) {
20439                 case 'trunk':
20440                 case 'motorway':
20441                     count = isOneWay ? 2 : 4;
20442                     break;
20443                 default:
20444                     count = isOneWay ? 1 : 2;
20445                     break;
20446             }
20447
20448             return count;
20449         }
20450
20451
20452         function parseMaxspeed(tags) {
20453             var maxspeed = tags.maxspeed;
20454             if (!maxspeed) return;
20455
20456             var maxspeedRegex = /^([0-9][\.0-9]+?)(?:[ ]?(?:km\/h|kmh|kph|mph|knots))?$/;
20457             if (!maxspeedRegex.test(maxspeed)) return;
20458
20459             return parseInt(maxspeed, 10);
20460         }
20461
20462
20463         function parseLaneDirections(tags, isOneWay, laneCount) {
20464             var forward = parseInt(tags['lanes:forward'], 10);
20465             var backward = parseInt(tags['lanes:backward'], 10);
20466             var bothways = parseInt(tags['lanes:both_ways'], 10) > 0 ? 1 : 0;
20467
20468             if (parseInt(tags.oneway, 10) === -1) {
20469                 forward = 0;
20470                 bothways = 0;
20471                 backward = laneCount;
20472             }
20473             else if (isOneWay) {
20474                 forward = laneCount;
20475                 bothways = 0;
20476                 backward = 0;
20477             }
20478             else if (isNaN(forward) && isNaN(backward)) {
20479                 backward = Math.floor((laneCount - bothways) / 2);
20480                 forward = laneCount - bothways - backward;
20481             }
20482             else if (isNaN(forward)) {
20483                 if (backward > laneCount - bothways) {
20484                     backward = laneCount - bothways;
20485                 }
20486                 forward = laneCount - bothways - backward;
20487             }
20488             else if (isNaN(backward)) {
20489                 if (forward > laneCount - bothways) {
20490                     forward = laneCount - bothways;
20491                 }
20492                 backward = laneCount - bothways - forward;
20493             }
20494             return {
20495                 forward: forward,
20496                 backward: backward,
20497                 bothways: bothways
20498             };
20499         }
20500
20501
20502         function parseTurnLanes(tag){
20503             if (!tag) return;
20504
20505             var validValues = [
20506                 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right',
20507                 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none'
20508             ];
20509
20510             return tag.split('|')
20511                 .map(function (s) {
20512                     if (s === '') s = 'none';
20513                     return s.split(';')
20514                         .map(function (d) {
20515                             return validValues.indexOf(d) === -1 ? 'unknown': d;
20516                         });
20517                 });
20518         }
20519
20520
20521         function parseMaxspeedLanes(tag, maxspeed) {
20522             if (!tag) return;
20523
20524             return tag.split('|')
20525                 .map(function (s) {
20526                     if (s === 'none') return s;
20527                     var m = parseInt(s, 10);
20528                     if (s === '' || m === maxspeed) return null;
20529                     return isNaN(m) ? 'unknown': m;
20530                 });
20531         }
20532
20533
20534         function parseMiscLanes(tag) {
20535             if (!tag) return;
20536
20537             var validValues = [
20538                 'yes', 'no', 'designated'
20539             ];
20540
20541             return tag.split('|')
20542                 .map(function (s) {
20543                     if (s === '') s = 'no';
20544                     return validValues.indexOf(s) === -1 ? 'unknown': s;
20545                 });
20546         }
20547
20548
20549         function parseBicycleWay(tag) {
20550             if (!tag) return;
20551
20552             var validValues = [
20553                 'yes', 'no', 'designated', 'lane'
20554             ];
20555
20556             return tag.split('|')
20557                 .map(function (s) {
20558                     if (s === '') s = 'no';
20559                     return validValues.indexOf(s) === -1 ? 'unknown': s;
20560                 });
20561         }
20562
20563
20564         function mapToLanesObj(lanesObj, data, key) {
20565             if (data.forward) data.forward.forEach(function(l, i) {
20566                 if (!lanesObj.forward[i]) lanesObj.forward[i] = {};
20567                 lanesObj.forward[i][key] = l;
20568             });
20569             if (data.backward) data.backward.forEach(function(l, i) {
20570                 if (!lanesObj.backward[i]) lanesObj.backward[i] = {};
20571                 lanesObj.backward[i][key] = l;
20572             });
20573             if (data.unspecified) data.unspecified.forEach(function(l, i) {
20574                 if (!lanesObj.unspecified[i]) lanesObj.unspecified[i] = {};
20575                 lanesObj.unspecified[i][key] = l;
20576             });
20577         }
20578
20579         function osmWay() {
20580             if (!(this instanceof osmWay)) {
20581                 return (new osmWay()).initialize(arguments);
20582             } else if (arguments.length) {
20583                 this.initialize(arguments);
20584             }
20585         }
20586
20587
20588         osmEntity.way = osmWay;
20589
20590         osmWay.prototype = Object.create(osmEntity.prototype);
20591
20592
20593         Object.assign(osmWay.prototype, {
20594             type: 'way',
20595             nodes: [],
20596
20597
20598             copy: function(resolver, copies) {
20599                 if (copies[this.id]) return copies[this.id];
20600
20601                 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
20602
20603                 var nodes = this.nodes.map(function(id) {
20604                     return resolver.entity(id).copy(resolver, copies).id;
20605                 });
20606
20607                 copy = copy.update({ nodes: nodes });
20608                 copies[this.id] = copy;
20609
20610                 return copy;
20611             },
20612
20613
20614             extent: function(resolver) {
20615                 return resolver.transient(this, 'extent', function() {
20616                     var extent = geoExtent();
20617                     for (var i = 0; i < this.nodes.length; i++) {
20618                         var node = resolver.hasEntity(this.nodes[i]);
20619                         if (node) {
20620                             extent._extend(node.extent());
20621                         }
20622                     }
20623                     return extent;
20624                 });
20625             },
20626
20627
20628             first: function() {
20629                 return this.nodes[0];
20630             },
20631
20632
20633             last: function() {
20634                 return this.nodes[this.nodes.length - 1];
20635             },
20636
20637
20638             contains: function(node) {
20639                 return this.nodes.indexOf(node) >= 0;
20640             },
20641
20642
20643             affix: function(node) {
20644                 if (this.nodes[0] === node) return 'prefix';
20645                 if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
20646             },
20647
20648
20649             layer: function() {
20650                 // explicit layer tag, clamp between -10, 10..
20651                 if (isFinite(this.tags.layer)) {
20652                     return Math.max(-10, Math.min(+(this.tags.layer), 10));
20653                 }
20654
20655                 // implied layer tag..
20656                 if (this.tags.covered === 'yes') return -1;
20657                 if (this.tags.location === 'overground') return 1;
20658                 if (this.tags.location === 'underground') return -1;
20659                 if (this.tags.location === 'underwater') return -10;
20660
20661                 if (this.tags.power === 'line') return 10;
20662                 if (this.tags.power === 'minor_line') return 10;
20663                 if (this.tags.aerialway) return 10;
20664                 if (this.tags.bridge) return 1;
20665                 if (this.tags.cutting) return -1;
20666                 if (this.tags.tunnel) return -1;
20667                 if (this.tags.waterway) return -1;
20668                 if (this.tags.man_made === 'pipeline') return -10;
20669                 if (this.tags.boundary) return -10;
20670                 return 0;
20671             },
20672
20673
20674             // the approximate width of the line based on its tags except its `width` tag
20675             impliedLineWidthMeters: function() {
20676                 var averageWidths = {
20677                     highway: { // width is for single lane
20678                         motorway: 5, motorway_link: 5, trunk: 4.5, trunk_link: 4.5,
20679                         primary: 4, secondary: 4, tertiary: 4,
20680                         primary_link: 4, secondary_link: 4, tertiary_link: 4,
20681                         unclassified: 4, road: 4, living_street: 4, bus_guideway: 4, pedestrian: 4,
20682                         residential: 3.5, service: 3.5, track: 3, cycleway: 2.5,
20683                         bridleway: 2, corridor: 2, steps: 2, path: 1.5, footway: 1.5
20684                     },
20685                     railway: { // width includes ties and rail bed, not just track gauge
20686                         rail: 2.5, light_rail: 2.5, tram: 2.5, subway: 2.5,
20687                         monorail: 2.5, funicular: 2.5, disused: 2.5, preserved: 2.5,
20688                         miniature: 1.5, narrow_gauge: 1.5
20689                     },
20690                     waterway: {
20691                         river: 50, canal: 25, stream: 5, tidal_channel: 5, fish_pass: 2.5, drain: 2.5, ditch: 1.5
20692                     }
20693                 };
20694                 for (var key in averageWidths) {
20695                     if (this.tags[key] && averageWidths[key][this.tags[key]]) {
20696                         var width = averageWidths[key][this.tags[key]];
20697                         if (key === 'highway') {
20698                             var laneCount = this.tags.lanes && parseInt(this.tags.lanes, 10);
20699                             if (!laneCount) laneCount = this.isOneWay() ? 1 : 2;
20700
20701                             return width * laneCount;
20702                         }
20703                         return width;
20704                     }
20705                 }
20706                 return null;
20707             },
20708
20709
20710             isOneWay: function() {
20711                 // explicit oneway tag..
20712                 var values = {
20713                     'yes': true,
20714                     '1': true,
20715                     '-1': true,
20716                     'reversible': true,
20717                     'alternating': true,
20718                     'no': false,
20719                     '0': false
20720                 };
20721                 if (values[this.tags.oneway] !== undefined) {
20722                     return values[this.tags.oneway];
20723                 }
20724
20725                 // implied oneway tag..
20726                 for (var key in this.tags) {
20727                     if (key in osmOneWayTags && (this.tags[key] in osmOneWayTags[key]))
20728                         return true;
20729                 }
20730                 return false;
20731             },
20732
20733             // Some identifier for tag that implies that this way is "sided",
20734             // i.e. the right side is the 'inside' (e.g. the right side of a
20735             // natural=cliff is lower).
20736             sidednessIdentifier: function() {
20737                 for (var key in this.tags) {
20738                     var value = this.tags[key];
20739                     if (key in osmRightSideIsInsideTags && (value in osmRightSideIsInsideTags[key])) {
20740                         if (osmRightSideIsInsideTags[key][value] === true) {
20741                             return key;
20742                         } else {
20743                             // if the map's value is something other than a
20744                             // literal true, we should use it so we can
20745                             // special case some keys (e.g. natural=coastline
20746                             // is handled differently to other naturals).
20747                             return osmRightSideIsInsideTags[key][value];
20748                         }
20749                     }
20750                 }
20751
20752                 return null;
20753             },
20754
20755             isSided: function() {
20756                 if (this.tags.two_sided === 'yes') {
20757                     return false;
20758                 }
20759
20760                 return this.sidednessIdentifier() !== null;
20761             },
20762
20763             lanes: function() {
20764                 return osmLanes(this);
20765             },
20766
20767
20768             isClosed: function() {
20769                 return this.nodes.length > 1 && this.first() === this.last();
20770             },
20771
20772
20773             isConvex: function(resolver) {
20774                 if (!this.isClosed() || this.isDegenerate()) return null;
20775
20776                 var nodes = utilArrayUniq(resolver.childNodes(this));
20777                 var coords = nodes.map(function(n) { return n.loc; });
20778                 var curr = 0;
20779                 var prev = 0;
20780
20781                 for (var i = 0; i < coords.length; i++) {
20782                     var o = coords[(i+1) % coords.length];
20783                     var a = coords[i];
20784                     var b = coords[(i+2) % coords.length];
20785                     var res = geoVecCross(a, b, o);
20786
20787                     curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
20788                     if (curr === 0) {
20789                         continue;
20790                     } else if (prev && curr !== prev) {
20791                         return false;
20792                     }
20793                     prev = curr;
20794                 }
20795                 return true;
20796             },
20797
20798             // returns an object with the tag that implies this is an area, if any
20799             tagSuggestingArea: function() {
20800                 return osmTagSuggestingArea(this.tags);
20801             },
20802
20803             isArea: function() {
20804                 if (this.tags.area === 'yes')
20805                     return true;
20806                 if (!this.isClosed() || this.tags.area === 'no')
20807                     return false;
20808                 return this.tagSuggestingArea() !== null;
20809             },
20810
20811
20812             isDegenerate: function() {
20813                 return (new Set(this.nodes).size < (this.isArea() ? 3 : 2));
20814             },
20815
20816
20817             areAdjacent: function(n1, n2) {
20818                 for (var i = 0; i < this.nodes.length; i++) {
20819                     if (this.nodes[i] === n1) {
20820                         if (this.nodes[i - 1] === n2) return true;
20821                         if (this.nodes[i + 1] === n2) return true;
20822                     }
20823                 }
20824                 return false;
20825             },
20826
20827
20828             geometry: function(graph) {
20829                 return graph.transient(this, 'geometry', function() {
20830                     return this.isArea() ? 'area' : 'line';
20831                 });
20832             },
20833
20834
20835             // returns an array of objects representing the segments between the nodes in this way
20836             segments: function(graph) {
20837
20838                 function segmentExtent(graph) {
20839                     var n1 = graph.hasEntity(this.nodes[0]);
20840                     var n2 = graph.hasEntity(this.nodes[1]);
20841                     return n1 && n2 && geoExtent([
20842                         [
20843                             Math.min(n1.loc[0], n2.loc[0]),
20844                             Math.min(n1.loc[1], n2.loc[1])
20845                         ],
20846                         [
20847                             Math.max(n1.loc[0], n2.loc[0]),
20848                             Math.max(n1.loc[1], n2.loc[1])
20849                         ]
20850                     ]);
20851                 }
20852
20853                 return graph.transient(this, 'segments', function() {
20854                     var segments = [];
20855                     for (var i = 0; i < this.nodes.length - 1; i++) {
20856                         segments.push({
20857                             id: this.id + '-' + i,
20858                             wayId: this.id,
20859                             index: i,
20860                             nodes: [this.nodes[i], this.nodes[i + 1]],
20861                             extent: segmentExtent
20862                         });
20863                     }
20864                     return segments;
20865                 });
20866             },
20867
20868
20869             // If this way is not closed, append the beginning node to the end of the nodelist to close it.
20870             close: function() {
20871                 if (this.isClosed() || !this.nodes.length) return this;
20872
20873                 var nodes = this.nodes.slice();
20874                 nodes = nodes.filter(noRepeatNodes);
20875                 nodes.push(nodes[0]);
20876                 return this.update({ nodes: nodes });
20877             },
20878
20879
20880             // If this way is closed, remove any connector nodes from the end of the nodelist to unclose it.
20881             unclose: function() {
20882                 if (!this.isClosed()) return this;
20883
20884                 var nodes = this.nodes.slice();
20885                 var connector = this.first();
20886                 var i = nodes.length - 1;
20887
20888                 // remove trailing connectors..
20889                 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
20890                     nodes.splice(i, 1);
20891                     i = nodes.length - 1;
20892                 }
20893
20894                 nodes = nodes.filter(noRepeatNodes);
20895                 return this.update({ nodes: nodes });
20896             },
20897
20898
20899             // Adds a node (id) in front of the node which is currently at position index.
20900             // If index is undefined, the node will be added to the end of the way for linear ways,
20901             //   or just before the final connecting node for circular ways.
20902             // Consecutive duplicates are eliminated including existing ones.
20903             // Circularity is always preserved when adding a node.
20904             addNode: function(id, index) {
20905                 var nodes = this.nodes.slice();
20906                 var isClosed = this.isClosed();
20907                 var max = isClosed ? nodes.length - 1 : nodes.length;
20908
20909                 if (index === undefined) {
20910                     index = max;
20911                 }
20912
20913                 if (index < 0 || index > max) {
20914                     throw new RangeError('index ' + index + ' out of range 0..' + max);
20915                 }
20916
20917                 // If this is a closed way, remove all connector nodes except the first one
20918                 // (there may be duplicates) and adjust index if necessary..
20919                 if (isClosed) {
20920                     var connector = this.first();
20921
20922                     // leading connectors..
20923                     var i = 1;
20924                     while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
20925                         nodes.splice(i, 1);
20926                         if (index > i) index--;
20927                     }
20928
20929                     // trailing connectors..
20930                     i = nodes.length - 1;
20931                     while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
20932                         nodes.splice(i, 1);
20933                         if (index > i) index--;
20934                         i = nodes.length - 1;
20935                     }
20936                 }
20937
20938                 nodes.splice(index, 0, id);
20939                 nodes = nodes.filter(noRepeatNodes);
20940
20941                 // If the way was closed before, append a connector node to keep it closed..
20942                 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
20943                     nodes.push(nodes[0]);
20944                 }
20945
20946                 return this.update({ nodes: nodes });
20947             },
20948
20949
20950             // Replaces the node which is currently at position index with the given node (id).
20951             // Consecutive duplicates are eliminated including existing ones.
20952             // Circularity is preserved when updating a node.
20953             updateNode: function(id, index) {
20954                 var nodes = this.nodes.slice();
20955                 var isClosed = this.isClosed();
20956                 var max = nodes.length - 1;
20957
20958                 if (index === undefined || index < 0 || index > max) {
20959                     throw new RangeError('index ' + index + ' out of range 0..' + max);
20960                 }
20961
20962                 // If this is a closed way, remove all connector nodes except the first one
20963                 // (there may be duplicates) and adjust index if necessary..
20964                 if (isClosed) {
20965                     var connector = this.first();
20966
20967                     // leading connectors..
20968                     var i = 1;
20969                     while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
20970                         nodes.splice(i, 1);
20971                         if (index > i) index--;
20972                     }
20973
20974                     // trailing connectors..
20975                     i = nodes.length - 1;
20976                     while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
20977                         nodes.splice(i, 1);
20978                         if (index === i) index = 0;  // update leading connector instead
20979                         i = nodes.length - 1;
20980                     }
20981                 }
20982
20983                 nodes.splice(index, 1, id);
20984                 nodes = nodes.filter(noRepeatNodes);
20985
20986                 // If the way was closed before, append a connector node to keep it closed..
20987                 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
20988                     nodes.push(nodes[0]);
20989                 }
20990
20991                 return this.update({nodes: nodes});
20992             },
20993
20994
20995             // Replaces each occurrence of node id needle with replacement.
20996             // Consecutive duplicates are eliminated including existing ones.
20997             // Circularity is preserved.
20998             replaceNode: function(needleID, replacementID) {
20999                 var nodes = this.nodes.slice();
21000                 var isClosed = this.isClosed();
21001
21002                 for (var i = 0; i < nodes.length; i++) {
21003                     if (nodes[i] === needleID) {
21004                         nodes[i] = replacementID;
21005                     }
21006                 }
21007
21008                 nodes = nodes.filter(noRepeatNodes);
21009
21010                 // If the way was closed before, append a connector node to keep it closed..
21011                 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
21012                     nodes.push(nodes[0]);
21013                 }
21014
21015                 return this.update({nodes: nodes});
21016             },
21017
21018
21019             // Removes each occurrence of node id.
21020             // Consecutive duplicates are eliminated including existing ones.
21021             // Circularity is preserved.
21022             removeNode: function(id) {
21023                 var nodes = this.nodes.slice();
21024                 var isClosed = this.isClosed();
21025
21026                 nodes = nodes
21027                     .filter(function(node) { return node !== id; })
21028                     .filter(noRepeatNodes);
21029
21030                 // If the way was closed before, append a connector node to keep it closed..
21031                 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
21032                     nodes.push(nodes[0]);
21033                 }
21034
21035                 return this.update({nodes: nodes});
21036             },
21037
21038
21039             asJXON: function(changeset_id) {
21040                 var r = {
21041                     way: {
21042                         '@id': this.osmId(),
21043                         '@version': this.version || 0,
21044                         nd: this.nodes.map(function(id) {
21045                             return { keyAttributes: { ref: osmEntity.id.toOSM(id) } };
21046                         }, this),
21047                         tag: Object.keys(this.tags).map(function(k) {
21048                             return { keyAttributes: { k: k, v: this.tags[k] } };
21049                         }, this)
21050                     }
21051                 };
21052                 if (changeset_id) {
21053                     r.way['@changeset'] = changeset_id;
21054                 }
21055                 return r;
21056             },
21057
21058
21059             asGeoJSON: function(resolver) {
21060                 return resolver.transient(this, 'GeoJSON', function() {
21061                     var coordinates = resolver.childNodes(this)
21062                         .map(function(n) { return n.loc; });
21063
21064                     if (this.isArea() && this.isClosed()) {
21065                         return {
21066                             type: 'Polygon',
21067                             coordinates: [coordinates]
21068                         };
21069                     } else {
21070                         return {
21071                             type: 'LineString',
21072                             coordinates: coordinates
21073                         };
21074                     }
21075                 });
21076             },
21077
21078
21079             area: function(resolver) {
21080                 return resolver.transient(this, 'area', function() {
21081                     var nodes = resolver.childNodes(this);
21082
21083                     var json = {
21084                         type: 'Polygon',
21085                         coordinates: [ nodes.map(function(n) { return n.loc; }) ]
21086                     };
21087
21088                     if (!this.isClosed() && nodes.length) {
21089                         json.coordinates[0].push(nodes[0].loc);
21090                     }
21091
21092                     var area = d3_geoArea(json);
21093
21094                     // Heuristic for detecting counterclockwise winding order. Assumes
21095                     // that OpenStreetMap polygons are not hemisphere-spanning.
21096                     if (area > 2 * Math.PI) {
21097                         json.coordinates[0] = json.coordinates[0].reverse();
21098                         area = d3_geoArea(json);
21099                     }
21100
21101                     return isNaN(area) ? 0 : area;
21102                 });
21103             }
21104         });
21105
21106
21107         // Filter function to eliminate consecutive duplicates.
21108         function noRepeatNodes(node, i, arr) {
21109             return i === 0 || node !== arr[i - 1];
21110         }
21111
21112         // "Old" multipolyons, previously known as "simple" multipolygons, are as follows:
21113         //
21114         // 1. Relation tagged with `type=multipolygon` and no interesting tags.
21115         // 2. One and only one member with the `outer` role. Must be a way with interesting tags.
21116         // 3. No members without a role.
21117         //
21118         // Old multipolygons are no longer recommended but are still rendered as areas by iD.
21119
21120         function osmOldMultipolygonOuterMemberOfRelation(entity, graph) {
21121             if (entity.type !== 'relation' ||
21122                 !entity.isMultipolygon()
21123                 || Object.keys(entity.tags).filter(osmIsInterestingTag).length > 1) {
21124                 return false;
21125             }
21126
21127             var outerMember;
21128             for (var memberIndex in entity.members) {
21129                 var member = entity.members[memberIndex];
21130                 if (!member.role || member.role === 'outer') {
21131                     if (outerMember) return false;
21132                     if (member.type !== 'way') return false;
21133                     if (!graph.hasEntity(member.id)) return false;
21134
21135                     outerMember = graph.entity(member.id);
21136
21137                     if (Object.keys(outerMember.tags).filter(osmIsInterestingTag).length === 0) {
21138                         return false;
21139                     }
21140                 }
21141             }
21142
21143             return outerMember;
21144         }
21145
21146         // For fixing up rendering of multipolygons with tags on the outer member.
21147         // https://github.com/openstreetmap/iD/issues/613
21148         function osmIsOldMultipolygonOuterMember(entity, graph) {
21149             if (entity.type !== 'way' || Object.keys(entity.tags).filter(osmIsInterestingTag).length === 0)
21150                 return false;
21151
21152             var parents = graph.parentRelations(entity);
21153             if (parents.length !== 1)
21154                 return false;
21155
21156             var parent = parents[0];
21157             if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1)
21158                 return false;
21159
21160             var members = parent.members, member;
21161             for (var i = 0; i < members.length; i++) {
21162                 member = members[i];
21163                 if (member.id === entity.id && member.role && member.role !== 'outer')
21164                     return false; // Not outer member
21165                 if (member.id !== entity.id && (!member.role || member.role === 'outer'))
21166                     return false; // Not a simple multipolygon
21167             }
21168
21169             return parent;
21170         }
21171
21172
21173         function osmOldMultipolygonOuterMember(entity, graph) {
21174             if (entity.type !== 'way')
21175                 return false;
21176
21177             var parents = graph.parentRelations(entity);
21178             if (parents.length !== 1)
21179                 return false;
21180
21181             var parent = parents[0];
21182             if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1)
21183                 return false;
21184
21185             var members = parent.members, member, outerMember;
21186             for (var i = 0; i < members.length; i++) {
21187                 member = members[i];
21188                 if (!member.role || member.role === 'outer') {
21189                     if (outerMember)
21190                         return false; // Not a simple multipolygon
21191                     outerMember = member;
21192                 }
21193             }
21194
21195             if (!outerMember)
21196                 return false;
21197
21198             var outerEntity = graph.hasEntity(outerMember.id);
21199             if (!outerEntity || !Object.keys(outerEntity.tags).filter(osmIsInterestingTag).length)
21200                 return false;
21201
21202             return outerEntity;
21203         }
21204
21205
21206         // Join `toJoin` array into sequences of connecting ways.
21207
21208         // Segments which share identical start/end nodes will, as much as possible,
21209         // be connected with each other.
21210         //
21211         // The return value is a nested array. Each constituent array contains elements
21212         // of `toJoin` which have been determined to connect.
21213         //
21214         // Each consitituent array also has a `nodes` property whose value is an
21215         // ordered array of member nodes, with appropriate order reversal and
21216         // start/end coordinate de-duplication.
21217         //
21218         // Members of `toJoin` must have, at minimum, `type` and `id` properties.
21219         // Thus either an array of `osmWay`s or a relation member array may be used.
21220         //
21221         // If an member is an `osmWay`, its tags and childnodes may be reversed via
21222         // `actionReverse` in the output.
21223         //
21224         // The returned sequences array also has an `actions` array property, containing
21225         // any reversal actions that should be applied to the graph, should the calling
21226         // code attempt to actually join the given ways.
21227         //
21228         // Incomplete members (those for which `graph.hasEntity(element.id)` returns
21229         // false) and non-way members are ignored.
21230         //
21231         function osmJoinWays(toJoin, graph) {
21232             function resolve(member) {
21233                 return graph.childNodes(graph.entity(member.id));
21234             }
21235
21236             function reverse(item) {
21237                 var action = actionReverse(item.id, { reverseOneway: true });
21238                 sequences.actions.push(action);
21239                 return (item instanceof osmWay) ? action(graph).entity(item.id) : item;
21240             }
21241
21242             // make a copy containing only the items to join
21243             toJoin = toJoin.filter(function(member) {
21244                 return member.type === 'way' && graph.hasEntity(member.id);
21245             });
21246
21247             // Are the things we are joining relation members or `osmWays`?
21248             // If `osmWays`, skip the "prefer a forward path" code below (see #4872)
21249             var i;
21250             var joinAsMembers = true;
21251             for (i = 0; i < toJoin.length; i++) {
21252                 if (toJoin[i] instanceof osmWay) {
21253                     joinAsMembers = false;
21254                     break;
21255                 }
21256             }
21257
21258             var sequences = [];
21259             sequences.actions = [];
21260
21261             while (toJoin.length) {
21262                 // start a new sequence
21263                 var item = toJoin.shift();
21264                 var currWays = [item];
21265                 var currNodes = resolve(item).slice();
21266                 var doneSequence = false;
21267
21268                 // add to it
21269                 while (toJoin.length && !doneSequence) {
21270                     var start = currNodes[0];
21271                     var end = currNodes[currNodes.length - 1];
21272                     var fn = null;
21273                     var nodes = null;
21274
21275                     // Find the next way/member to join.
21276                     for (i = 0; i < toJoin.length; i++) {
21277                         item = toJoin[i];
21278                         nodes = resolve(item);
21279
21280                         // (for member ordering only, not way ordering - see #4872)
21281                         // Strongly prefer to generate a forward path that preserves the order
21282                         // of the members array. For multipolygons and most relations, member
21283                         // order does not matter - but for routes, it does. (see #4589)
21284                         // If we started this sequence backwards (i.e. next member way attaches to
21285                         // the start node and not the end node), reverse the initial way before continuing.
21286                         if (joinAsMembers && currWays.length === 1 && nodes[0] !== end && nodes[nodes.length - 1] !== end &&
21287                             (nodes[nodes.length - 1] === start || nodes[0] === start)
21288                         ) {
21289                             currWays[0] = reverse(currWays[0]);
21290                             currNodes.reverse();
21291                             start = currNodes[0];
21292                             end = currNodes[currNodes.length - 1];
21293                         }
21294
21295                         if (nodes[0] === end) {
21296                             fn = currNodes.push;               // join to end
21297                             nodes = nodes.slice(1);
21298                             break;
21299                         } else if (nodes[nodes.length - 1] === end) {
21300                             fn = currNodes.push;               // join to end
21301                             nodes = nodes.slice(0, -1).reverse();
21302                             item = reverse(item);
21303                             break;
21304                         } else if (nodes[nodes.length - 1] === start) {
21305                             fn = currNodes.unshift;            // join to beginning
21306                             nodes = nodes.slice(0, -1);
21307                             break;
21308                         } else if (nodes[0] === start) {
21309                             fn = currNodes.unshift;            // join to beginning
21310                             nodes = nodes.slice(1).reverse();
21311                             item = reverse(item);
21312                             break;
21313                         } else {
21314                             fn = nodes = null;
21315                         }
21316                     }
21317
21318                     if (!nodes) {     // couldn't find a joinable way/member
21319                         doneSequence = true;
21320                         break;
21321                     }
21322
21323                     fn.apply(currWays, [item]);
21324                     fn.apply(currNodes, nodes);
21325
21326                     toJoin.splice(i, 1);
21327                 }
21328
21329                 currWays.nodes = currNodes;
21330                 sequences.push(currWays);
21331             }
21332
21333             return sequences;
21334         }
21335
21336         function actionAddMember(relationId, member, memberIndex, insertPair) {
21337
21338             return function action(graph) {
21339                 var relation = graph.entity(relationId);
21340
21341                 // There are some special rules for Public Transport v2 routes.
21342                 var isPTv2 = /stop|platform/.test(member.role);
21343
21344                 if ((isNaN(memberIndex) || insertPair) && member.type === 'way' && !isPTv2) {
21345                     // Try to perform sensible inserts based on how the ways join together
21346                     graph = addWayMember(relation, graph);
21347                 } else {
21348                     // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
21349                     // Stops and Platforms for PTv2 should be ordered first.
21350                     // hack: We do not currently have the ability to place them in the exactly correct order.
21351                     if (isPTv2 && isNaN(memberIndex)) {
21352                         memberIndex = 0;
21353                     }
21354
21355                     graph = graph.replace(relation.addMember(member, memberIndex));
21356                 }
21357
21358                 return graph;
21359             };
21360
21361
21362             // Add a way member into the relation "wherever it makes sense".
21363             // In this situation we were not supplied a memberIndex.
21364             function addWayMember(relation, graph) {
21365                 var groups, tempWay, item, i, j, k;
21366
21367                 // remove PTv2 stops and platforms before doing anything.
21368                 var PTv2members = [];
21369                 var members = [];
21370                 for (i = 0; i < relation.members.length; i++) {
21371                     var m = relation.members[i];
21372                     if (/stop|platform/.test(m.role)) {
21373                         PTv2members.push(m);
21374                     } else {
21375                         members.push(m);
21376                     }
21377                 }
21378                 relation = relation.update({ members: members });
21379
21380
21381                 if (insertPair) {
21382                     // We're adding a member that must stay paired with an existing member.
21383                     // (This feature is used by `actionSplit`)
21384                     //
21385                     // This is tricky because the members may exist multiple times in the
21386                     // member list, and with different A-B/B-A ordering and different roles.
21387                     // (e.g. a bus route that loops out and back - #4589).
21388                     //
21389                     // Replace the existing member with a temporary way,
21390                     // so that `osmJoinWays` can treat the pair like a single way.
21391                     tempWay = osmWay({ id: 'wTemp', nodes: insertPair.nodes });
21392                     graph = graph.replace(tempWay);
21393                     var tempMember = { id: tempWay.id, type: 'way', role: member.role };
21394                     var tempRelation = relation.replaceMember({id: insertPair.originalID}, tempMember, true);
21395                     groups = utilArrayGroupBy(tempRelation.members, 'type');
21396                     groups.way = groups.way || [];
21397
21398                 } else {
21399                     // Add the member anywhere, one time. Just push and let `osmJoinWays` decide where to put it.
21400                     groups = utilArrayGroupBy(relation.members, 'type');
21401                     groups.way = groups.way || [];
21402                     groups.way.push(member);
21403                 }
21404
21405                 members = withIndex(groups.way);
21406                 var joined = osmJoinWays(members, graph);
21407
21408                 // `joined` might not contain all of the way members,
21409                 // But will contain only the completed (downloaded) members
21410                 for (i = 0; i < joined.length; i++) {
21411                     var segment = joined[i];
21412                     var nodes = segment.nodes.slice();
21413                     var startIndex = segment[0].index;
21414
21415                     // j = array index in `members` where this segment starts
21416                     for (j = 0; j < members.length; j++) {
21417                         if (members[j].index === startIndex) {
21418                             break;
21419                         }
21420                     }
21421
21422                     // k = each member in segment
21423                     for (k = 0; k < segment.length; k++) {
21424                         item = segment[k];
21425                         var way = graph.entity(item.id);
21426
21427                         // If this is a paired item, generate members in correct order and role
21428                         if (tempWay && item.id === tempWay.id) {
21429                             if (nodes[0].id === insertPair.nodes[0]) {
21430                                 item.pair = [
21431                                     { id: insertPair.originalID, type: 'way', role: item.role },
21432                                     { id: insertPair.insertedID, type: 'way', role: item.role }
21433                                 ];
21434                             } else {
21435                                 item.pair = [
21436                                     { id: insertPair.insertedID, type: 'way', role: item.role },
21437                                     { id: insertPair.originalID, type: 'way', role: item.role }
21438                                 ];
21439                             }
21440                         }
21441
21442                         // reorder `members` if necessary
21443                         if (k > 0) {
21444                             if (j+k >= members.length || item.index !== members[j+k].index) {
21445                                 moveMember(members, item.index, j+k);
21446                             }
21447                         }
21448
21449                         nodes.splice(0, way.nodes.length - 1);
21450                     }
21451                 }
21452
21453                 if (tempWay) {
21454                     graph = graph.remove(tempWay);
21455                 }
21456
21457                 // Final pass: skip dead items, split pairs, remove index properties
21458                 var wayMembers = [];
21459                 for (i = 0; i < members.length; i++) {
21460                     item = members[i];
21461                     if (item.index === -1) continue;
21462
21463                     if (item.pair) {
21464                         wayMembers.push(item.pair[0]);
21465                         wayMembers.push(item.pair[1]);
21466                     } else {
21467                         wayMembers.push(utilObjectOmit(item, ['index']));
21468                     }
21469                 }
21470
21471                 // Put stops and platforms first, then nodes, ways, relations
21472                 // This is recommended for Public Transport v2 routes:
21473                 // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
21474                 var newMembers = PTv2members.concat( (groups.node || []), wayMembers, (groups.relation || []) );
21475
21476                 return graph.replace(relation.update({ members: newMembers }));
21477
21478
21479                 // `moveMember()` changes the `members` array in place by splicing
21480                 // the item with `.index = findIndex` to where it belongs,
21481                 // and marking the old position as "dead" with `.index = -1`
21482                 //
21483                 // j=5, k=0                jk
21484                 // segment                 5 4 7 6
21485                 // members       0 1 2 3 4 5 6 7 8 9        keep 5 in j+k
21486                 //
21487                 // j=5, k=1                j k
21488                 // segment                 5 4 7 6
21489                 // members       0 1 2 3 4 5 6 7 8 9        move 4 to j+k
21490                 // members       0 1 2 3 x 5 4 6 7 8 9      moved
21491                 //
21492                 // j=5, k=2                j   k
21493                 // segment                 5 4 7 6
21494                 // members       0 1 2 3 x 5 4 6 7 8 9      move 7 to j+k
21495                 // members       0 1 2 3 x 5 4 7 6 x 8 9    moved
21496                 //
21497                 // j=5, k=3                j     k
21498                 // segment                 5 4 7 6
21499                 // members       0 1 2 3 x 5 4 7 6 x 8 9    keep 6 in j+k
21500                 //
21501                 function moveMember(arr, findIndex, toIndex) {
21502                     for (var i = 0; i < arr.length; i++) {
21503                         if (arr[i].index === findIndex) {
21504                             break;
21505                         }
21506                     }
21507
21508                     var item = Object.assign({}, arr[i]);   // shallow copy
21509                     arr[i].index = -1;   // mark as dead
21510                     item.index = toIndex;
21511                     arr.splice(toIndex, 0, item);
21512                 }
21513
21514
21515                 // This is the same as `Relation.indexedMembers`,
21516                 // Except we don't want to index all the members, only the ways
21517                 function withIndex(arr) {
21518                     var result = new Array(arr.length);
21519                     for (var i = 0; i < arr.length; i++) {
21520                         result[i] = Object.assign({}, arr[i]);   // shallow copy
21521                         result[i].index = i;
21522                     }
21523                     return result;
21524                 }
21525             }
21526
21527         }
21528
21529         function actionAddMidpoint(midpoint, node) {
21530             return function(graph) {
21531                 graph = graph.replace(node.move(midpoint.loc));
21532
21533                 var parents = utilArrayIntersection(
21534                     graph.parentWays(graph.entity(midpoint.edge[0])),
21535                     graph.parentWays(graph.entity(midpoint.edge[1]))
21536                 );
21537
21538                 parents.forEach(function(way) {
21539                     for (var i = 0; i < way.nodes.length - 1; i++) {
21540                         if (geoEdgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
21541                             graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1));
21542
21543                             // Add only one midpoint on doubled-back segments,
21544                             // turning them into self-intersections.
21545                             return;
21546                         }
21547                     }
21548                 });
21549
21550                 return graph;
21551             };
21552         }
21553
21554         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
21555         function actionAddVertex(wayId, nodeId, index) {
21556             return function(graph) {
21557                 return graph.replace(graph.entity(wayId).addNode(nodeId, index));
21558             };
21559         }
21560
21561         function actionChangeMember(relationId, member, memberIndex) {
21562             return function(graph) {
21563                 return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
21564             };
21565         }
21566
21567         function actionChangePreset(entityID, oldPreset, newPreset, skipFieldDefaults) {
21568             return function action(graph) {
21569                 var entity = graph.entity(entityID);
21570                 var geometry = entity.geometry(graph);
21571                 var tags = entity.tags;
21572
21573                 if (oldPreset) tags = oldPreset.unsetTags(tags, geometry);
21574                 if (newPreset) tags = newPreset.setTags(tags, geometry, skipFieldDefaults);
21575
21576                 return graph.replace(entity.update({tags: tags}));
21577             };
21578         }
21579
21580         function actionChangeTags(entityId, tags) {
21581             return function(graph) {
21582                 var entity = graph.entity(entityId);
21583                 return graph.replace(entity.update({tags: tags}));
21584             };
21585         }
21586
21587         function osmNode() {
21588             if (!(this instanceof osmNode)) {
21589                 return (new osmNode()).initialize(arguments);
21590             } else if (arguments.length) {
21591                 this.initialize(arguments);
21592             }
21593         }
21594
21595         osmEntity.node = osmNode;
21596
21597         osmNode.prototype = Object.create(osmEntity.prototype);
21598
21599         Object.assign(osmNode.prototype, {
21600             type: 'node',
21601             loc: [9999, 9999],
21602
21603             extent: function() {
21604                 return new geoExtent(this.loc);
21605             },
21606
21607
21608             geometry: function(graph) {
21609                 return graph.transient(this, 'geometry', function() {
21610                     return graph.isPoi(this) ? 'point' : 'vertex';
21611                 });
21612             },
21613
21614
21615             move: function(loc) {
21616                 return this.update({loc: loc});
21617             },
21618
21619
21620             isDegenerate: function() {
21621                 return !(
21622                     Array.isArray(this.loc) && this.loc.length === 2 &&
21623                     this.loc[0] >= -180 && this.loc[0] <= 180 &&
21624                     this.loc[1] >= -90 && this.loc[1] <= 90
21625                 );
21626             },
21627
21628
21629             // Inspect tags and geometry to determine which direction(s) this node/vertex points
21630             directions: function(resolver, projection) {
21631                 var val;
21632                 var i;
21633
21634                 // which tag to use?
21635                 if (this.isHighwayIntersection(resolver) && (this.tags.stop || '').toLowerCase() === 'all') {
21636                     // all-way stop tag on a highway intersection
21637                     val = 'all';
21638                 } else {
21639                     // generic direction tag
21640                     val = (this.tags.direction || '').toLowerCase();
21641
21642                     // better suffix-style direction tag
21643                     var re = /:direction$/i;
21644                     var keys = Object.keys(this.tags);
21645                     for (i = 0; i < keys.length; i++) {
21646                         if (re.test(keys[i])) {
21647                             val = this.tags[keys[i]].toLowerCase();
21648                             break;
21649                         }
21650                     }
21651                 }
21652
21653                 if (val === '') return [];
21654
21655                 var cardinal = {
21656                     north: 0,               n: 0,
21657                     northnortheast: 22,     nne: 22,
21658                     northeast: 45,          ne: 45,
21659                     eastnortheast: 67,      ene: 67,
21660                     east: 90,               e: 90,
21661                     eastsoutheast: 112,     ese: 112,
21662                     southeast: 135,         se: 135,
21663                     southsoutheast: 157,    sse: 157,
21664                     south: 180,             s: 180,
21665                     southsouthwest: 202,    ssw: 202,
21666                     southwest: 225,         sw: 225,
21667                     westsouthwest: 247,     wsw: 247,
21668                     west: 270,              w: 270,
21669                     westnorthwest: 292,     wnw: 292,
21670                     northwest: 315,         nw: 315,
21671                     northnorthwest: 337,    nnw: 337
21672                 };
21673
21674
21675                 var values = val.split(';');
21676                 var results = [];
21677
21678                 values.forEach(function(v) {
21679                     // swap cardinal for numeric directions
21680                     if (cardinal[v] !== undefined) {
21681                         v = cardinal[v];
21682                     }
21683
21684                     // numeric direction - just add to results
21685                     if (v !== '' && !isNaN(+v)) {
21686                         results.push(+v);
21687                         return;
21688                     }
21689
21690                     // string direction - inspect parent ways
21691                     var lookBackward =
21692                         (this.tags['traffic_sign:backward'] || v === 'backward' || v === 'both' || v === 'all');
21693                     var lookForward =
21694                         (this.tags['traffic_sign:forward'] || v === 'forward' || v === 'both' || v === 'all');
21695
21696                     if (!lookForward && !lookBackward) return;
21697
21698                     var nodeIds = {};
21699                     resolver.parentWays(this).forEach(function(parent) {
21700                         var nodes = parent.nodes;
21701                         for (i = 0; i < nodes.length; i++) {
21702                             if (nodes[i] === this.id) {  // match current entity
21703                                 if (lookForward && i > 0) {
21704                                     nodeIds[nodes[i - 1]] = true;  // look back to prev node
21705                                 }
21706                                 if (lookBackward && i < nodes.length - 1) {
21707                                     nodeIds[nodes[i + 1]] = true;  // look ahead to next node
21708                                 }
21709                             }
21710                         }
21711                     }, this);
21712
21713                     Object.keys(nodeIds).forEach(function(nodeId) {
21714                         // +90 because geoAngle returns angle from X axis, not Y (north)
21715                         results.push(
21716                             (geoAngle(this, resolver.entity(nodeId), projection) * (180 / Math.PI)) + 90
21717                         );
21718                     }, this);
21719
21720                 }, this);
21721
21722                 return utilArrayUniq(results);
21723             },
21724
21725
21726             isEndpoint: function(resolver) {
21727                 return resolver.transient(this, 'isEndpoint', function() {
21728                     var id = this.id;
21729                     return resolver.parentWays(this).filter(function(parent) {
21730                         return !parent.isClosed() && !!parent.affix(id);
21731                     }).length > 0;
21732                 });
21733             },
21734
21735
21736             isConnected: function(resolver) {
21737                 return resolver.transient(this, 'isConnected', function() {
21738                     var parents = resolver.parentWays(this);
21739
21740                     if (parents.length > 1) {
21741                         // vertex is connected to multiple parent ways
21742                         for (var i in parents) {
21743                             if (parents[i].geometry(resolver) === 'line' &&
21744                                 parents[i].hasInterestingTags()) return true;
21745                         }
21746                     } else if (parents.length === 1) {
21747                         var way = parents[0];
21748                         var nodes = way.nodes.slice();
21749                         if (way.isClosed()) { nodes.pop(); }  // ignore connecting node if closed
21750
21751                         // return true if vertex appears multiple times (way is self intersecting)
21752                         return nodes.indexOf(this.id) !== nodes.lastIndexOf(this.id);
21753                     }
21754
21755                     return false;
21756                 });
21757             },
21758
21759
21760             parentIntersectionWays: function(resolver) {
21761                 return resolver.transient(this, 'parentIntersectionWays', function() {
21762                     return resolver.parentWays(this).filter(function(parent) {
21763                         return (parent.tags.highway ||
21764                             parent.tags.waterway ||
21765                             parent.tags.railway ||
21766                             parent.tags.aeroway) &&
21767                             parent.geometry(resolver) === 'line';
21768                     });
21769                 });
21770             },
21771
21772
21773             isIntersection: function(resolver) {
21774                 return this.parentIntersectionWays(resolver).length > 1;
21775             },
21776
21777
21778             isHighwayIntersection: function(resolver) {
21779                 return resolver.transient(this, 'isHighwayIntersection', function() {
21780                     return resolver.parentWays(this).filter(function(parent) {
21781                         return parent.tags.highway && parent.geometry(resolver) === 'line';
21782                     }).length > 1;
21783                 });
21784             },
21785
21786
21787             isOnAddressLine: function(resolver) {
21788                 return resolver.transient(this, 'isOnAddressLine', function() {
21789                     return resolver.parentWays(this).filter(function(parent) {
21790                         return parent.tags.hasOwnProperty('addr:interpolation') &&
21791                             parent.geometry(resolver) === 'line';
21792                     }).length > 0;
21793                 });
21794             },
21795
21796
21797             asJXON: function(changeset_id) {
21798                 var r = {
21799                     node: {
21800                         '@id': this.osmId(),
21801                         '@lon': this.loc[0],
21802                         '@lat': this.loc[1],
21803                         '@version': (this.version || 0),
21804                         tag: Object.keys(this.tags).map(function(k) {
21805                             return { keyAttributes: { k: k, v: this.tags[k] } };
21806                         }, this)
21807                     }
21808                 };
21809                 if (changeset_id) r.node['@changeset'] = changeset_id;
21810                 return r;
21811             },
21812
21813
21814             asGeoJSON: function() {
21815                 return {
21816                     type: 'Point',
21817                     coordinates: this.loc
21818                 };
21819             }
21820         });
21821
21822         function actionCircularize(wayId, projection, maxAngle) {
21823             maxAngle = (maxAngle || 20) * Math.PI / 180;
21824
21825
21826             var action = function(graph, t) {
21827                 if (t === null || !isFinite(t)) t = 1;
21828                 t = Math.min(Math.max(+t, 0), 1);
21829
21830                 var way = graph.entity(wayId);
21831                 var origNodes = {};
21832
21833                 graph.childNodes(way).forEach(function(node) {
21834                     if (!origNodes[node.id]) origNodes[node.id] = node;
21835                 });
21836
21837                 if (!way.isConvex(graph)) {
21838                     graph = action.makeConvex(graph);
21839                 }
21840
21841                 var nodes = utilArrayUniq(graph.childNodes(way));
21842                 var keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; });
21843                 var points = nodes.map(function(n) { return projection(n.loc); });
21844                 var keyPoints = keyNodes.map(function(n) { return projection(n.loc); });
21845                 var centroid = (points.length === 2) ? geoVecInterp(points[0], points[1], 0.5) : d3_polygonCentroid(points);
21846                 var radius = d3_median(points, function(p) { return geoVecLength(centroid, p); });
21847                 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
21848                 var ids, i, j, k;
21849
21850                 // we need atleast two key nodes for the algorithm to work
21851                 if (!keyNodes.length) {
21852                     keyNodes = [nodes[0]];
21853                     keyPoints = [points[0]];
21854                 }
21855
21856                 if (keyNodes.length === 1) {
21857                     var index = nodes.indexOf(keyNodes[0]);
21858                     var oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
21859
21860                     keyNodes.push(nodes[oppositeIndex]);
21861                     keyPoints.push(points[oppositeIndex]);
21862                 }
21863
21864                 // key points and nodes are those connected to the ways,
21865                 // they are projected onto the circle, inbetween nodes are moved
21866                 // to constant intervals between key nodes, extra inbetween nodes are
21867                 // added if necessary.
21868                 for (i = 0; i < keyPoints.length; i++) {
21869                     var nextKeyNodeIndex = (i + 1) % keyNodes.length;
21870                     var startNode = keyNodes[i];
21871                     var endNode = keyNodes[nextKeyNodeIndex];
21872                     var startNodeIndex = nodes.indexOf(startNode);
21873                     var endNodeIndex = nodes.indexOf(endNode);
21874                     var numberNewPoints = -1;
21875                     var indexRange = endNodeIndex - startNodeIndex;
21876                     var nearNodes = {};
21877                     var inBetweenNodes = [];
21878                     var startAngle, endAngle, totalAngle, eachAngle;
21879                     var angle, loc, node, origNode;
21880
21881                     if (indexRange < 0) {
21882                         indexRange += nodes.length;
21883                     }
21884
21885                     // position this key node
21886                     var distance = geoVecLength(centroid, keyPoints[i]) || 1e-4;
21887                     keyPoints[i] = [
21888                         centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius,
21889                         centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius
21890                     ];
21891                     loc = projection.invert(keyPoints[i]);
21892                     node = keyNodes[i];
21893                     origNode = origNodes[node.id];
21894                     node = node.move(geoVecInterp(origNode.loc, loc, t));
21895                     graph = graph.replace(node);
21896
21897                     // figure out the between delta angle we want to match to
21898                     startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
21899                     endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
21900                     totalAngle = endAngle - startAngle;
21901
21902                     // detects looping around -pi/pi
21903                     if (totalAngle * sign > 0) {
21904                         totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
21905                     }
21906
21907                     do {
21908                         numberNewPoints++;
21909                         eachAngle = totalAngle / (indexRange + numberNewPoints);
21910                     } while (Math.abs(eachAngle) > maxAngle);
21911
21912
21913                     // move existing nodes
21914                     for (j = 1; j < indexRange; j++) {
21915                         angle = startAngle + j * eachAngle;
21916                         loc = projection.invert([
21917                             centroid[0] + Math.cos(angle) * radius,
21918                             centroid[1] + Math.sin(angle) * radius
21919                         ]);
21920
21921                         node = nodes[(j + startNodeIndex) % nodes.length];
21922                         origNode = origNodes[node.id];
21923                         nearNodes[node.id] = angle;
21924
21925                         node = node.move(geoVecInterp(origNode.loc, loc, t));
21926                         graph = graph.replace(node);
21927                     }
21928
21929                     // add new inbetween nodes if necessary
21930                     for (j = 0; j < numberNewPoints; j++) {
21931                         angle = startAngle + (indexRange + j) * eachAngle;
21932                         loc = projection.invert([
21933                             centroid[0] + Math.cos(angle) * radius,
21934                             centroid[1] + Math.sin(angle) * radius
21935                         ]);
21936
21937                         // choose a nearnode to use as the original
21938                         var min = Infinity;
21939                         for (var nodeId in nearNodes) {
21940                             var nearAngle = nearNodes[nodeId];
21941                             var dist = Math.abs(nearAngle - angle);
21942                             if (dist < min) {
21943                                 dist = min;
21944                                 origNode = origNodes[nodeId];
21945                             }
21946                         }
21947
21948                         node = osmNode({ loc: geoVecInterp(origNode.loc, loc, t) });
21949                         graph = graph.replace(node);
21950
21951                         nodes.splice(endNodeIndex + j, 0, node);
21952                         inBetweenNodes.push(node.id);
21953                     }
21954
21955                     // Check for other ways that share these keyNodes..
21956                     // If keyNodes are adjacent in both ways,
21957                     // we can add inBetween nodes to that shared way too..
21958                     if (indexRange === 1 && inBetweenNodes.length) {
21959                         var startIndex1 = way.nodes.lastIndexOf(startNode.id);
21960                         var endIndex1 = way.nodes.lastIndexOf(endNode.id);
21961                         var wayDirection1 = (endIndex1 - startIndex1);
21962                         if (wayDirection1 < -1) { wayDirection1 = 1; }
21963
21964                         var parentWays = graph.parentWays(keyNodes[i]);
21965                         for (j = 0; j < parentWays.length; j++) {
21966                             var sharedWay = parentWays[j];
21967                             if (sharedWay === way) continue;
21968
21969                             if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
21970                                 var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id);
21971                                 var endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id);
21972                                 var wayDirection2 = (endIndex2 - startIndex2);
21973                                 var insertAt = endIndex2;
21974                                 if (wayDirection2 < -1) { wayDirection2 = 1; }
21975
21976                                 if (wayDirection1 !== wayDirection2) {
21977                                     inBetweenNodes.reverse();
21978                                     insertAt = startIndex2;
21979                                 }
21980                                 for (k = 0; k < inBetweenNodes.length; k++) {
21981                                     sharedWay = sharedWay.addNode(inBetweenNodes[k], insertAt + k);
21982                                 }
21983                                 graph = graph.replace(sharedWay);
21984                             }
21985                         }
21986                     }
21987
21988                 }
21989
21990                 // update the way to have all the new nodes
21991                 ids = nodes.map(function(n) { return n.id; });
21992                 ids.push(ids[0]);
21993
21994                 way = way.update({nodes: ids});
21995                 graph = graph.replace(way);
21996
21997                 return graph;
21998             };
21999
22000
22001             action.makeConvex = function(graph) {
22002                 var way = graph.entity(wayId);
22003                 var nodes = utilArrayUniq(graph.childNodes(way));
22004                 var points = nodes.map(function(n) { return projection(n.loc); });
22005                 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
22006                 var hull = d3_polygonHull(points);
22007                 var i, j;
22008
22009                 // D3 convex hulls go counterclockwise..
22010                 if (sign === -1) {
22011                     nodes.reverse();
22012                     points.reverse();
22013                 }
22014
22015                 for (i = 0; i < hull.length - 1; i++) {
22016                     var startIndex = points.indexOf(hull[i]);
22017                     var endIndex = points.indexOf(hull[i+1]);
22018                     var indexRange = (endIndex - startIndex);
22019
22020                     if (indexRange < 0) {
22021                         indexRange += nodes.length;
22022                     }
22023
22024                     // move interior nodes to the surface of the convex hull..
22025                     for (j = 1; j < indexRange; j++) {
22026                         var point = geoVecInterp(hull[i], hull[i+1], j / indexRange);
22027                         var node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
22028                         graph = graph.replace(node);
22029                     }
22030                 }
22031                 return graph;
22032             };
22033
22034
22035             action.disabled = function(graph) {
22036                 if (!graph.entity(wayId).isClosed()) {
22037                     return 'not_closed';
22038                 }
22039
22040                 //disable when already circular
22041                 var way = graph.entity(wayId);
22042                 var nodes = utilArrayUniq(graph.childNodes(way));
22043                 var points = nodes.map(function(n) { return projection(n.loc); });
22044                 var hull = d3_polygonHull(points);
22045                 var epsilonAngle =  Math.PI / 180;
22046                 if (hull.length !== points.length || hull.length < 3){
22047                     return false;
22048                 }
22049                 var centroid = d3_polygonCentroid(points);
22050                 var radius = geoVecLengthSquare(centroid, points[0]);
22051
22052                 // compare distances between centroid and points
22053                 for (var i = 0; i<hull.length; i++){
22054                     var actualPoint = hull[i];
22055                     var actualDist = geoVecLengthSquare(actualPoint, centroid);
22056                     var diff = Math.abs(actualDist - radius);
22057                     //compare distances with epsilon-error (5%)
22058                     if (diff > 0.05*radius) {
22059                         return false;
22060                     }
22061                 }
22062                 
22063                 //check if central angles are smaller than maxAngle
22064                 for (i = 0; i<hull.length; i++){
22065                     actualPoint = hull[i];
22066                     var nextPoint = hull[(i+1)%hull.length];
22067                     var startAngle = Math.atan2(actualPoint[1] - centroid[1], actualPoint[0] - centroid[0]);
22068                     var endAngle = Math.atan2(nextPoint[1] - centroid[1], nextPoint[0] - centroid[0]);
22069                     var angle = endAngle - startAngle;
22070                     if (angle < 0) {
22071                         angle = -angle;
22072                     }
22073                     if (angle > Math.PI){
22074                         angle = (2*Math.PI - angle);
22075                     }
22076          
22077                     if (angle > maxAngle + epsilonAngle) {
22078                         return false;
22079                     }
22080                 }
22081                 return 'already_circular';
22082             };
22083
22084
22085             action.transitionable = true;
22086
22087
22088             return action;
22089         }
22090
22091         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
22092         function actionDeleteWay(wayID) {
22093
22094             function canDeleteNode(node, graph) {
22095                 // don't delete nodes still attached to ways or relations
22096                 if (graph.parentWays(node).length ||
22097                     graph.parentRelations(node).length) return false;
22098
22099                 var geometries = osmNodeGeometriesForTags(node.tags);
22100                 // don't delete if this node can be a standalone point
22101                 if (geometries.point) return false;
22102                 // delete if this node only be a vertex
22103                 if (geometries.vertex) return true;
22104
22105                 // iD doesn't know if this should be a point or vertex,
22106                 // so only delete if there are no interesting tags
22107                 return !node.hasInterestingTags();
22108             }
22109
22110
22111             var action = function(graph) {
22112                 var way = graph.entity(wayID);
22113
22114                 graph.parentRelations(way).forEach(function(parent) {
22115                     parent = parent.removeMembersWithID(wayID);
22116                     graph = graph.replace(parent);
22117
22118                     if (parent.isDegenerate()) {
22119                         graph = actionDeleteRelation(parent.id)(graph);
22120                     }
22121                 });
22122
22123                 (new Set(way.nodes)).forEach(function(nodeID) {
22124                     graph = graph.replace(way.removeNode(nodeID));
22125
22126                     var node = graph.entity(nodeID);
22127                     if (canDeleteNode(node, graph)) {
22128                         graph = graph.remove(node);
22129                     }
22130                 });
22131
22132                 return graph.remove(way);
22133             };
22134
22135
22136             return action;
22137         }
22138
22139         function actionDeleteMultiple(ids) {
22140             var actions = {
22141                 way: actionDeleteWay,
22142                 node: actionDeleteNode,
22143                 relation: actionDeleteRelation
22144             };
22145
22146
22147             var action = function(graph) {
22148                 ids.forEach(function(id) {
22149                     if (graph.hasEntity(id)) { // It may have been deleted aready.
22150                         graph = actions[graph.entity(id).type](id)(graph);
22151                     }
22152                 });
22153
22154                 return graph;
22155             };
22156
22157
22158             return action;
22159         }
22160
22161         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteRelationAction.as
22162         function actionDeleteRelation(relationID, allowUntaggedMembers) {
22163
22164             function canDeleteEntity(entity, graph) {
22165                 return !graph.parentWays(entity).length &&
22166                     !graph.parentRelations(entity).length &&
22167                     (!entity.hasInterestingTags() && !allowUntaggedMembers);
22168             }
22169
22170
22171             var action = function(graph) {
22172                 var relation = graph.entity(relationID);
22173
22174                 graph.parentRelations(relation)
22175                     .forEach(function(parent) {
22176                         parent = parent.removeMembersWithID(relationID);
22177                         graph = graph.replace(parent);
22178
22179                         if (parent.isDegenerate()) {
22180                             graph = actionDeleteRelation(parent.id)(graph);
22181                         }
22182                     });
22183
22184                 var memberIDs = utilArrayUniq(relation.members.map(function(m) { return m.id; }));
22185                 memberIDs.forEach(function(memberID) {
22186                     graph = graph.replace(relation.removeMembersWithID(memberID));
22187
22188                     var entity = graph.entity(memberID);
22189                     if (canDeleteEntity(entity, graph)) {
22190                         graph = actionDeleteMultiple([memberID])(graph);
22191                     }
22192                 });
22193
22194                 return graph.remove(relation);
22195             };
22196
22197
22198             return action;
22199         }
22200
22201         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as
22202         function actionDeleteNode(nodeId) {
22203             var action = function(graph) {
22204                 var node = graph.entity(nodeId);
22205
22206                 graph.parentWays(node)
22207                     .forEach(function(parent) {
22208                         parent = parent.removeNode(nodeId);
22209                         graph = graph.replace(parent);
22210
22211                         if (parent.isDegenerate()) {
22212                             graph = actionDeleteWay(parent.id)(graph);
22213                         }
22214                     });
22215
22216                 graph.parentRelations(node)
22217                     .forEach(function(parent) {
22218                         parent = parent.removeMembersWithID(nodeId);
22219                         graph = graph.replace(parent);
22220
22221                         if (parent.isDegenerate()) {
22222                             graph = actionDeleteRelation(parent.id)(graph);
22223                         }
22224                     });
22225
22226                 return graph.remove(node);
22227             };
22228
22229
22230             return action;
22231         }
22232
22233         // Connect the ways at the given nodes.
22234         //
22235         // First choose a node to be the survivor, with preference given
22236         // to an existing (not new) node.
22237         //
22238         // Tags and relation memberships of of non-surviving nodes are merged
22239         // to the survivor.
22240         //
22241         // This is the inverse of `iD.actionDisconnect`.
22242         //
22243         // Reference:
22244         //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
22245         //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
22246         //
22247         function actionConnect(nodeIDs) {
22248             var action = function(graph) {
22249                 var survivor;
22250                 var node;
22251                 var parents;
22252                 var i, j;
22253
22254                 // Choose a survivor node, prefer an existing (not new) node - #4974
22255                 for (i = 0; i < nodeIDs.length; i++) {
22256                     survivor = graph.entity(nodeIDs[i]);
22257                     if (survivor.version) break;  // found one
22258                 }
22259
22260                 // Replace all non-surviving nodes with the survivor and merge tags.
22261                 for (i = 0; i < nodeIDs.length; i++) {
22262                     node = graph.entity(nodeIDs[i]);
22263                     if (node.id === survivor.id) continue;
22264
22265                     parents = graph.parentWays(node);
22266                     for (j = 0; j < parents.length; j++) {
22267                         graph = graph.replace(parents[j].replaceNode(node.id, survivor.id));
22268                     }
22269
22270                     parents = graph.parentRelations(node);
22271                     for (j = 0; j < parents.length; j++) {
22272                         graph = graph.replace(parents[j].replaceMember(node, survivor));
22273                     }
22274
22275                     survivor = survivor.mergeTags(node.tags);
22276                     graph = actionDeleteNode(node.id)(graph);
22277                 }
22278
22279                 graph = graph.replace(survivor);
22280
22281                 // find and delete any degenerate ways created by connecting adjacent vertices
22282                 parents = graph.parentWays(survivor);
22283                 for (i = 0; i < parents.length; i++) {
22284                     if (parents[i].isDegenerate()) {
22285                         graph = actionDeleteWay(parents[i].id)(graph);
22286                     }
22287                 }
22288
22289                 return graph;
22290             };
22291
22292
22293             action.disabled = function(graph) {
22294                 var seen = {};
22295                 var restrictionIDs = [];
22296                 var survivor;
22297                 var node, way;
22298                 var relations, relation, role;
22299                 var i, j, k;
22300
22301                 // Choose a survivor node, prefer an existing (not new) node - #4974
22302                 for (i = 0; i < nodeIDs.length; i++) {
22303                     survivor = graph.entity(nodeIDs[i]);
22304                     if (survivor.version) break;  // found one
22305                 }
22306
22307                 // 1. disable if the nodes being connected have conflicting relation roles
22308                 for (i = 0; i < nodeIDs.length; i++) {
22309                     node = graph.entity(nodeIDs[i]);
22310                     relations = graph.parentRelations(node);
22311
22312                     for (j = 0; j < relations.length; j++) {
22313                         relation = relations[j];
22314                         role = relation.memberById(node.id).role || '';
22315
22316                         // if this node is a via node in a restriction, remember for later
22317                         if (relation.hasFromViaTo()) {
22318                             restrictionIDs.push(relation.id);
22319                         }
22320
22321                         if (seen[relation.id] !== undefined && seen[relation.id] !== role) {
22322                             return 'relation';
22323                         } else {
22324                             seen[relation.id] = role;
22325                         }
22326                     }
22327                 }
22328
22329                 // gather restrictions for parent ways
22330                 for (i = 0; i < nodeIDs.length; i++) {
22331                     node = graph.entity(nodeIDs[i]);
22332
22333                     var parents = graph.parentWays(node);
22334                     for (j = 0; j < parents.length; j++) {
22335                         var parent = parents[j];
22336                         relations = graph.parentRelations(parent);
22337
22338                         for (k = 0; k < relations.length; k++) {
22339                             relation = relations[k];
22340                             if (relation.hasFromViaTo()) {
22341                                 restrictionIDs.push(relation.id);
22342                             }
22343                         }
22344                     }
22345                 }
22346
22347
22348                 // test restrictions
22349                 restrictionIDs = utilArrayUniq(restrictionIDs);
22350                 for (i = 0; i < restrictionIDs.length; i++) {
22351                     relation = graph.entity(restrictionIDs[i]);
22352                     if (!relation.isComplete(graph)) continue;
22353
22354                     var memberWays = relation.members
22355                         .filter(function(m) { return m.type === 'way'; })
22356                         .map(function(m) { return graph.entity(m.id); });
22357
22358                     memberWays = utilArrayUniq(memberWays);
22359                     var f = relation.memberByRole('from');
22360                     var t = relation.memberByRole('to');
22361                     var isUturn = (f.id === t.id);
22362
22363                     // 2a. disable if connection would damage a restriction
22364                     // (a key node is a node at the junction of ways)
22365                     var nodes = { from: [], via: [], to: [], keyfrom: [], keyto: [] };
22366                     for (j = 0; j < relation.members.length; j++) {
22367                         collectNodes(relation.members[j], nodes);
22368                     }
22369
22370                     nodes.keyfrom = utilArrayUniq(nodes.keyfrom.filter(hasDuplicates));
22371                     nodes.keyto = utilArrayUniq(nodes.keyto.filter(hasDuplicates));
22372
22373                     var filter = keyNodeFilter(nodes.keyfrom, nodes.keyto);
22374                     nodes.from = nodes.from.filter(filter);
22375                     nodes.via = nodes.via.filter(filter);
22376                     nodes.to = nodes.to.filter(filter);
22377
22378                     var connectFrom = false;
22379                     var connectVia = false;
22380                     var connectTo = false;
22381                     var connectKeyFrom = false;
22382                     var connectKeyTo = false;
22383
22384                     for (j = 0; j < nodeIDs.length; j++) {
22385                         var n = nodeIDs[j];
22386                         if (nodes.from.indexOf(n) !== -1)    { connectFrom = true; }
22387                         if (nodes.via.indexOf(n) !== -1)     { connectVia = true; }
22388                         if (nodes.to.indexOf(n) !== -1)      { connectTo = true; }
22389                         if (nodes.keyfrom.indexOf(n) !== -1) { connectKeyFrom = true; }
22390                         if (nodes.keyto.indexOf(n) !== -1)   { connectKeyTo = true; }
22391                     }
22392                     if (connectFrom && connectTo && !isUturn) { return 'restriction'; }
22393                     if (connectFrom && connectVia) { return 'restriction'; }
22394                     if (connectTo   && connectVia) { return 'restriction'; }
22395
22396                     // connecting to a key node -
22397                     // if both nodes are on a member way (i.e. part of the turn restriction),
22398                     // the connecting node must be adjacent to the key node.
22399                     if (connectKeyFrom || connectKeyTo) {
22400                         if (nodeIDs.length !== 2) { return 'restriction'; }
22401
22402                         var n0 = null;
22403                         var n1 = null;
22404                         for (j = 0; j < memberWays.length; j++) {
22405                             way = memberWays[j];
22406                             if (way.contains(nodeIDs[0])) { n0 = nodeIDs[0]; }
22407                             if (way.contains(nodeIDs[1])) { n1 = nodeIDs[1]; }
22408                         }
22409
22410                         if (n0 && n1) {    // both nodes are part of the restriction
22411                             var ok = false;
22412                             for (j = 0; j < memberWays.length; j++) {
22413                                 way = memberWays[j];
22414                                 if (way.areAdjacent(n0, n1)) {
22415                                     ok = true;
22416                                     break;
22417                                 }
22418                             }
22419                             if (!ok) {
22420                                 return 'restriction';
22421                             }
22422                         }
22423                     }
22424
22425                     // 2b. disable if nodes being connected will destroy a member way in a restriction
22426                     // (to test, make a copy and try actually connecting the nodes)
22427                     for (j = 0; j < memberWays.length; j++) {
22428                         way = memberWays[j].update({});   // make copy
22429                         for (k = 0; k < nodeIDs.length; k++) {
22430                             if (nodeIDs[k] === survivor.id) continue;
22431
22432                             if (way.areAdjacent(nodeIDs[k], survivor.id)) {
22433                                 way = way.removeNode(nodeIDs[k]);
22434                             } else {
22435                                 way = way.replaceNode(nodeIDs[k], survivor.id);
22436                             }
22437                         }
22438                         if (way.isDegenerate()) {
22439                             return 'restriction';
22440                         }
22441                     }
22442                 }
22443
22444                 return false;
22445
22446
22447                 // if a key node appears multiple times (indexOf !== lastIndexOf) it's a FROM-VIA or TO-VIA junction
22448                 function hasDuplicates(n, i, arr) {
22449                     return arr.indexOf(n) !== arr.lastIndexOf(n);
22450                 }
22451
22452                 function keyNodeFilter(froms, tos) {
22453                     return function(n) {
22454                         return froms.indexOf(n) === -1 && tos.indexOf(n) === -1;
22455                     };
22456                 }
22457
22458                 function collectNodes(member, collection) {
22459                     var entity = graph.hasEntity(member.id);
22460                     if (!entity) return;
22461
22462                     var role = member.role || '';
22463                     if (!collection[role]) {
22464                         collection[role] = [];
22465                     }
22466
22467                     if (member.type === 'node') {
22468                         collection[role].push(member.id);
22469                         if (role === 'via') {
22470                             collection.keyfrom.push(member.id);
22471                             collection.keyto.push(member.id);
22472                         }
22473
22474                     } else if (member.type === 'way') {
22475                         collection[role].push.apply(collection[role], entity.nodes);
22476                         if (role === 'from' || role === 'via') {
22477                             collection.keyfrom.push(entity.first());
22478                             collection.keyfrom.push(entity.last());
22479                         }
22480                         if (role === 'to' || role === 'via') {
22481                             collection.keyto.push(entity.first());
22482                             collection.keyto.push(entity.last());
22483                         }
22484                     }
22485                 }
22486             };
22487
22488
22489             return action;
22490         }
22491
22492         function actionCopyEntities(ids, fromGraph) {
22493             var _copies = {};
22494
22495
22496             var action = function(graph) {
22497                 ids.forEach(function(id) {
22498                     fromGraph.entity(id).copy(fromGraph, _copies);
22499                 });
22500
22501                 for (var id in _copies) {
22502                     graph = graph.replace(_copies[id]);
22503                 }
22504
22505                 return graph;
22506             };
22507
22508
22509             action.copies = function() {
22510                 return _copies;
22511             };
22512
22513
22514             return action;
22515         }
22516
22517         function actionDeleteMember(relationId, memberIndex) {
22518             return function(graph) {
22519                 var relation = graph.entity(relationId)
22520                     .removeMember(memberIndex);
22521
22522                 graph = graph.replace(relation);
22523
22524                 if (relation.isDegenerate())
22525                     graph = actionDeleteRelation(relation.id)(graph);
22526
22527                 return graph;
22528             };
22529         }
22530
22531         function actionDiscardTags(difference, discardTags) {
22532           discardTags = discardTags || {};
22533
22534           return (graph) => {
22535             difference.modified().forEach(checkTags);
22536             difference.created().forEach(checkTags);
22537             return graph;
22538
22539             function checkTags(entity) {
22540               const keys = Object.keys(entity.tags);
22541               let didDiscard = false;
22542               let tags = {};
22543
22544               for (let i = 0; i < keys.length; i++) {
22545                 const k = keys[i];
22546                 if (discardTags[k] || !entity.tags[k]) {
22547                   didDiscard = true;
22548                 } else {
22549                   tags[k] = entity.tags[k];
22550                 }
22551               }
22552               if (didDiscard) {
22553                 graph = graph.replace(entity.update({ tags: tags }));
22554               }
22555             }
22556
22557           };
22558         }
22559
22560         // Disconect the ways at the given node.
22561         //
22562         // Optionally, disconnect only the given ways.
22563         //
22564         // For testing convenience, accepts an ID to assign to the (first) new node.
22565         // Normally, this will be undefined and the way will automatically
22566         // be assigned a new ID.
22567         //
22568         // This is the inverse of `iD.actionConnect`.
22569         //
22570         // Reference:
22571         //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
22572         //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
22573         //
22574         function actionDisconnect(nodeId, newNodeId) {
22575             var wayIds;
22576
22577
22578             var action = function(graph) {
22579                 var node = graph.entity(nodeId);
22580                 var connections = action.connections(graph);
22581
22582                 connections.forEach(function(connection) {
22583                     var way = graph.entity(connection.wayID);
22584                     var newNode = osmNode({id: newNodeId, loc: node.loc, tags: node.tags});
22585
22586                     graph = graph.replace(newNode);
22587                     if (connection.index === 0 && way.isArea()) {
22588                         // replace shared node with shared node..
22589                         graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
22590                     } else if (way.isClosed() && connection.index === way.nodes.length - 1) {
22591                         // replace closing node with new new node..
22592                         graph = graph.replace(way.unclose().addNode(newNode.id));
22593                     } else {
22594                         // replace shared node with multiple new nodes..
22595                         graph = graph.replace(way.updateNode(newNode.id, connection.index));
22596                     }
22597                 });
22598
22599                 return graph;
22600             };
22601
22602
22603             action.connections = function(graph) {
22604                 var candidates = [];
22605                 var keeping = false;
22606                 var parentWays = graph.parentWays(graph.entity(nodeId));
22607                 var way, waynode;
22608                 for (var i = 0; i < parentWays.length; i++) {
22609                     way = parentWays[i];
22610                     if (wayIds && wayIds.indexOf(way.id) === -1) {
22611                         keeping = true;
22612                         continue;
22613                     }
22614                     if (way.isArea() && (way.nodes[0] === nodeId)) {
22615                         candidates.push({ wayID: way.id, index: 0 });
22616                     } else {
22617                         for (var j = 0; j < way.nodes.length; j++) {
22618                             waynode = way.nodes[j];
22619                             if (waynode === nodeId) {
22620                                 if (way.isClosed() &&
22621                                     parentWays.length > 1 &&
22622                                     wayIds &&
22623                                     wayIds.indexOf(way.id) !== -1 &&
22624                                     j === way.nodes.length - 1) {
22625                                     continue;
22626                                 }
22627                                 candidates.push({ wayID: way.id, index: j });
22628                             }
22629                         }
22630                     }
22631                 }
22632
22633                 return keeping ? candidates : candidates.slice(1);
22634             };
22635
22636
22637             action.disabled = function(graph) {
22638                 var connections = action.connections(graph);
22639                 if (connections.length === 0)
22640                     return 'not_connected';
22641
22642                 var parentWays = graph.parentWays(graph.entity(nodeId));
22643                 var seenRelationIds = {};
22644                 var sharedRelation;
22645
22646                 parentWays.forEach(function(way) {
22647                     var relations = graph.parentRelations(way);
22648                     relations.forEach(function(relation) {
22649                         if (relation.id in seenRelationIds) {
22650                             if (wayIds) {
22651                                 if (wayIds.indexOf(way.id) !== -1 ||
22652                                     wayIds.indexOf(seenRelationIds[relation.id]) !== -1) {
22653                                     sharedRelation = relation;
22654                                 }
22655                             } else {
22656                                 sharedRelation = relation;
22657                             }
22658                         } else {
22659                             seenRelationIds[relation.id] = way.id;
22660                         }
22661                     });
22662                 });
22663
22664                 if (sharedRelation)
22665                     return 'relation';
22666             };
22667
22668
22669             action.limitWays = function(val) {
22670                 if (!arguments.length) return wayIds;
22671                 wayIds = val;
22672                 return action;
22673             };
22674
22675
22676             return action;
22677         }
22678
22679         function actionExtract(entityID) {
22680
22681             var extractedNodeID;
22682
22683             var action = function(graph) {
22684                 var entity = graph.entity(entityID);
22685
22686                 if (entity.type === 'node') {
22687                     return extractFromNode(entity, graph);
22688                 }
22689
22690                 return extractFromWayOrRelation(entity, graph);
22691             };
22692
22693             function extractFromNode(node, graph) {
22694
22695                 extractedNodeID = node.id;
22696
22697                 // Create a new node to replace the one we will detach
22698                 var replacement = osmNode({ loc: node.loc });
22699                 graph = graph.replace(replacement);
22700
22701                 // Process each way in turn, updating the graph as we go
22702                 graph = graph.parentWays(node)
22703                     .reduce(function(accGraph, parentWay) {
22704                         return accGraph.replace(parentWay.replaceNode(entityID, replacement.id));
22705                     }, graph);
22706
22707                 // Process any relations too
22708                 return graph.parentRelations(node)
22709                     .reduce(function(accGraph, parentRel) {
22710                         return accGraph.replace(parentRel.replaceMember(node, replacement));
22711                     }, graph);
22712             }
22713
22714             function extractFromWayOrRelation(entity, graph) {
22715
22716                 var fromGeometry = entity.geometry(graph);
22717
22718                 var keysToCopyAndRetain = ['source', 'wheelchair'];
22719                 var keysToRetain = ['area'];
22720                 var buildingKeysToRetain = ['architect', 'building', 'height', 'layer'];
22721
22722                 var extractedLoc = d3_geoCentroid(entity.asGeoJSON(graph));
22723                 if (!extractedLoc  || !isFinite(extractedLoc[0]) || !isFinite(extractedLoc[1])) {
22724                     extractedLoc = entity.extent(graph).center();
22725                 }
22726
22727                 var isBuilding = entity.tags.building && entity.tags.building !== 'no';
22728
22729                 var entityTags = Object.assign({}, entity.tags);  // shallow copy
22730                 var pointTags = {};
22731                 for (var key in entityTags) {
22732
22733                     if (entity.type === 'relation' &&
22734                         key === 'type') {
22735                         continue;
22736                     }
22737
22738                     if (keysToRetain.indexOf(key) !== -1) {
22739                         continue;
22740                     }
22741
22742                     if (isBuilding) {
22743                         // don't transfer building-related tags
22744                         if (buildingKeysToRetain.indexOf(key) !== -1 ||
22745                             key.match(/^building:.{1,}/) ||
22746                             key.match(/^roof:.{1,}/)) continue;
22747                     }
22748
22749                     // copy the tag from the entity to the point
22750                     pointTags[key] = entityTags[key];
22751
22752                     // leave addresses and some other tags so they're on both features
22753                     if (keysToCopyAndRetain.indexOf(key) !== -1 ||
22754                         key.match(/^addr:.{1,}/)) {
22755                         continue;
22756                     }
22757
22758                     // remove the tag from the entity
22759                     delete entityTags[key];
22760                 }
22761
22762                 if (!isBuilding && fromGeometry === 'area') {
22763                     // ensure that areas keep area geometry
22764                     entityTags.area = 'yes';
22765                 }
22766
22767                 var replacement = osmNode({ loc: extractedLoc, tags: pointTags });
22768                 graph = graph.replace(replacement);
22769
22770                 extractedNodeID = replacement.id;
22771
22772                 return graph.replace(entity.update({tags: entityTags}));
22773             }
22774
22775             action.getExtractedNodeID = function() {
22776                 return extractedNodeID;
22777             };
22778
22779             return action;
22780         }
22781
22782         // Join ways at the end node they share.
22783         //
22784         // This is the inverse of `iD.actionSplit`.
22785         //
22786         // Reference:
22787         //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
22788         //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
22789         //
22790         function actionJoin(ids) {
22791
22792             function groupEntitiesByGeometry(graph) {
22793                 var entities = ids.map(function(id) { return graph.entity(id); });
22794                 return Object.assign(
22795                     { line: [] },
22796                     utilArrayGroupBy(entities, function(entity) { return entity.geometry(graph); })
22797                 );
22798             }
22799
22800
22801             var action = function(graph) {
22802                 var ways = ids.map(graph.entity, graph);
22803                 var survivorID = ways[0].id;
22804
22805                 // if any of the ways are sided (e.g. coastline, cliff, kerb)
22806                 // sort them first so they establish the overall order - #6033
22807                 ways.sort(function(a, b) {
22808                     var aSided = a.isSided();
22809                     var bSided = b.isSided();
22810                     return (aSided && !bSided) ? -1
22811                         : (bSided && !aSided) ? 1
22812                         : 0;
22813                 });
22814
22815                 // Prefer to keep an existing way.
22816                 for (var i = 0; i < ways.length; i++) {
22817                     if (!ways[i].isNew()) {
22818                         survivorID = ways[i].id;
22819                         break;
22820                     }
22821                 }
22822
22823                 var sequences = osmJoinWays(ways, graph);
22824                 var joined = sequences[0];
22825
22826                 // We might need to reverse some of these ways before joining them.  #4688
22827                 // `joined.actions` property will contain any actions we need to apply.
22828                 graph = sequences.actions.reduce(function(g, action) { return action(g); }, graph);
22829
22830                 var survivor = graph.entity(survivorID);
22831                 survivor = survivor.update({ nodes: joined.nodes.map(function(n) { return n.id; }) });
22832                 graph = graph.replace(survivor);
22833
22834                 joined.forEach(function(way) {
22835                     if (way.id === survivorID) return;
22836
22837                     graph.parentRelations(way).forEach(function(parent) {
22838                         graph = graph.replace(parent.replaceMember(way, survivor));
22839                     });
22840
22841                     survivor = survivor.mergeTags(way.tags);
22842
22843                     graph = graph.replace(survivor);
22844                     graph = actionDeleteWay(way.id)(graph);
22845                 });
22846
22847                 // Finds if the join created a single-member multipolygon,
22848                 // and if so turns it into a basic area instead
22849                 function checkForSimpleMultipolygon() {
22850                     if (!survivor.isClosed()) return;
22851
22852                     var multipolygons = graph.parentMultipolygons(survivor).filter(function(multipolygon) {
22853                         // find multipolygons where the survivor is the only member
22854                         return multipolygon.members.length === 1;
22855                     });
22856
22857                     // skip if this is the single member of multiple multipolygons
22858                     if (multipolygons.length !== 1) return;
22859
22860                     var multipolygon = multipolygons[0];
22861
22862                     for (var key in survivor.tags) {
22863                         if (multipolygon.tags[key] &&
22864                             // don't collapse if tags cannot be cleanly merged
22865                             multipolygon.tags[key] !== survivor.tags[key]) return;
22866                     }
22867
22868                     survivor = survivor.mergeTags(multipolygon.tags);
22869                     graph = graph.replace(survivor);
22870                     graph = actionDeleteRelation(multipolygon.id, true /* allow untagged members */)(graph);
22871
22872                     var tags = Object.assign({}, survivor.tags);
22873                     if (survivor.geometry(graph) !== 'area') {
22874                         // ensure the feature persists as an area
22875                         tags.area = 'yes';
22876                     }
22877                     delete tags.type; // remove type=multipolygon
22878                     survivor = survivor.update({ tags: tags });
22879                     graph = graph.replace(survivor);
22880                 }
22881                 checkForSimpleMultipolygon();
22882
22883                 return graph;
22884             };
22885
22886             // Returns the number of nodes the resultant way is expected to have
22887             action.resultingWayNodesLength = function(graph) {
22888                 return ids.reduce(function(count, id) {
22889                     return count + graph.entity(id).nodes.length;
22890                 }, 0) - ids.length - 1;
22891             };
22892
22893
22894             action.disabled = function(graph) {
22895                 var geometries = groupEntitiesByGeometry(graph);
22896                 if (ids.length < 2 || ids.length !== geometries.line.length) {
22897                     return 'not_eligible';
22898                 }
22899
22900                 var joined = osmJoinWays(ids.map(graph.entity, graph), graph);
22901                 if (joined.length > 1) {
22902                     return 'not_adjacent';
22903                 }
22904
22905                 // Loop through all combinations of path-pairs
22906                 // to check potential intersections between all pairs
22907                 for (var i = 0; i < ids.length - 1; i++) {
22908                     for (var j = i + 1; j < ids.length; j++) {
22909                         var path1 = graph.childNodes(graph.entity(ids[i]))
22910                             .map(function(e) { return e.loc; });
22911                         var path2 = graph.childNodes(graph.entity(ids[j]))
22912                             .map(function(e) { return e.loc; });
22913                         var intersections = geoPathIntersections(path1, path2);
22914
22915                         // Check if intersections are just nodes lying on top of
22916                         // each other/the line, as opposed to crossing it
22917                         var common = utilArrayIntersection(
22918                             joined[0].nodes.map(function(n) { return n.loc.toString(); }),
22919                             intersections.map(function(n) { return n.toString(); })
22920                         );
22921                         if (common.length !== intersections.length) {
22922                             return 'paths_intersect';
22923                         }
22924                     }
22925                 }
22926
22927                 var nodeIds = joined[0].nodes.map(function(n) { return n.id; }).slice(1, -1);
22928                 var relation;
22929                 var tags = {};
22930                 var conflicting = false;
22931
22932                 joined[0].forEach(function(way) {
22933                     var parents = graph.parentRelations(way);
22934                     parents.forEach(function(parent) {
22935                         if (parent.isRestriction() && parent.members.some(function(m) { return nodeIds.indexOf(m.id) >= 0; })) {
22936                             relation = parent;
22937                         }
22938                     });
22939
22940                     for (var k in way.tags) {
22941                         if (!(k in tags)) {
22942                             tags[k] = way.tags[k];
22943                         } else if (tags[k] && osmIsInterestingTag(k) && tags[k] !== way.tags[k]) {
22944                             conflicting = true;
22945                         }
22946                     }
22947                 });
22948
22949                 if (relation) {
22950                     return 'restriction';
22951                 }
22952
22953                 if (conflicting) {
22954                     return 'conflicting_tags';
22955                 }
22956             };
22957
22958
22959             return action;
22960         }
22961
22962         function actionMerge(ids) {
22963
22964             function groupEntitiesByGeometry(graph) {
22965                 var entities = ids.map(function(id) { return graph.entity(id); });
22966                 return Object.assign(
22967                     { point: [], area: [], line: [], relation: [] },
22968                     utilArrayGroupBy(entities, function(entity) { return entity.geometry(graph); })
22969                 );
22970             }
22971
22972
22973             var action = function(graph) {
22974                 var geometries = groupEntitiesByGeometry(graph);
22975                 var target = geometries.area[0] || geometries.line[0];
22976                 var points = geometries.point;
22977
22978                 points.forEach(function(point) {
22979                     target = target.mergeTags(point.tags);
22980                     graph = graph.replace(target);
22981
22982                     graph.parentRelations(point).forEach(function(parent) {
22983                         graph = graph.replace(parent.replaceMember(point, target));
22984                     });
22985
22986                     var nodes = utilArrayUniq(graph.childNodes(target));
22987                     var removeNode = point;
22988
22989                     for (var i = 0; i < nodes.length; i++) {
22990                         var node = nodes[i];
22991                         if (graph.parentWays(node).length > 1 ||
22992                             graph.parentRelations(node).length ||
22993                             node.hasInterestingTags()) {
22994                             continue;
22995                         }
22996
22997                         // Found an uninteresting child node on the target way.
22998                         // Move orig point into its place to preserve point's history. #3683
22999                         graph = graph.replace(point.update({ tags: {}, loc: node.loc }));
23000                         target = target.replaceNode(node.id, point.id);
23001                         graph = graph.replace(target);
23002                         removeNode = node;
23003                         break;
23004                     }
23005
23006                     graph = graph.remove(removeNode);
23007                 });
23008
23009                 if (target.tags.area === 'yes') {
23010                     var tags = Object.assign({}, target.tags); // shallow copy
23011                     delete tags.area;
23012                     if (osmTagSuggestingArea(tags)) {
23013                         // remove the `area` tag if area geometry is now implied - #3851
23014                         target = target.update({ tags: tags });
23015                         graph = graph.replace(target);
23016                     }
23017                 }
23018
23019                 return graph;
23020             };
23021
23022
23023             action.disabled = function(graph) {
23024                 var geometries = groupEntitiesByGeometry(graph);
23025                 if (geometries.point.length === 0 ||
23026                     (geometries.area.length + geometries.line.length) !== 1 ||
23027                     geometries.relation.length !== 0) {
23028                     return 'not_eligible';
23029                 }
23030             };
23031
23032
23033             return action;
23034         }
23035
23036         // `actionMergeNodes` is just a combination of:
23037         //
23038         // 1. move all the nodes to a common location
23039         // 2. `actionConnect` them
23040
23041         function actionMergeNodes(nodeIDs, loc) {
23042
23043             // If there is a single "interesting" node, use that as the location.
23044             // Otherwise return the average location of all the nodes.
23045             function chooseLoc(graph) {
23046                 if (!nodeIDs.length) return null;
23047                 var sum = [0,0];
23048                 var interestingCount = 0;
23049                 var interestingLoc;
23050
23051                 for (var i = 0; i < nodeIDs.length; i++) {
23052                     var node = graph.entity(nodeIDs[i]);
23053                     if (node.hasInterestingTags()) {
23054                         interestingLoc = (++interestingCount === 1) ? node.loc : null;
23055                     }
23056                     sum = geoVecAdd(sum, node.loc);
23057                 }
23058
23059                 return interestingLoc || geoVecScale(sum, 1 / nodeIDs.length);
23060             }
23061
23062
23063             var action = function(graph) {
23064                 if (nodeIDs.length < 2) return graph;
23065                 var toLoc = loc;
23066                 if (!toLoc) {
23067                     toLoc = chooseLoc(graph);
23068                 }
23069
23070                 for (var i = 0; i < nodeIDs.length; i++) {
23071                     var node = graph.entity(nodeIDs[i]);
23072                     if (node.loc !== toLoc) {
23073                         graph = graph.replace(node.move(toLoc));
23074                     }
23075                 }
23076
23077                 return actionConnect(nodeIDs)(graph);
23078             };
23079
23080
23081             action.disabled = function(graph) {
23082                 if (nodeIDs.length < 2) return 'not_eligible';
23083
23084                 for (var i = 0; i < nodeIDs.length; i++) {
23085                     var entity = graph.entity(nodeIDs[i]);
23086                     if (entity.type !== 'node') return 'not_eligible';
23087                 }
23088
23089                 return actionConnect(nodeIDs).disabled(graph);
23090             };
23091
23092             return action;
23093         }
23094
23095         function osmChangeset() {
23096             if (!(this instanceof osmChangeset)) {
23097                 return (new osmChangeset()).initialize(arguments);
23098             } else if (arguments.length) {
23099                 this.initialize(arguments);
23100             }
23101         }
23102
23103
23104         osmEntity.changeset = osmChangeset;
23105
23106         osmChangeset.prototype = Object.create(osmEntity.prototype);
23107
23108         Object.assign(osmChangeset.prototype, {
23109
23110             type: 'changeset',
23111
23112
23113             extent: function() {
23114                 return new geoExtent();
23115             },
23116
23117
23118             geometry: function() {
23119                 return 'changeset';
23120             },
23121
23122
23123             asJXON: function() {
23124                 return {
23125                     osm: {
23126                         changeset: {
23127                             tag: Object.keys(this.tags).map(function(k) {
23128                                 return { '@k': k, '@v': this.tags[k] };
23129                             }, this),
23130                             '@version': 0.6,
23131                             '@generator': 'iD'
23132                         }
23133                     }
23134                 };
23135             },
23136
23137
23138             // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
23139             // XML. Returns a string.
23140             osmChangeJXON: function(changes) {
23141                 var changeset_id = this.id;
23142
23143                 function nest(x, order) {
23144                     var groups = {};
23145                     for (var i = 0; i < x.length; i++) {
23146                         var tagName = Object.keys(x[i])[0];
23147                         if (!groups[tagName]) groups[tagName] = [];
23148                         groups[tagName].push(x[i][tagName]);
23149                     }
23150                     var ordered = {};
23151                     order.forEach(function(o) {
23152                         if (groups[o]) ordered[o] = groups[o];
23153                     });
23154                     return ordered;
23155                 }
23156
23157
23158                 // sort relations in a changeset by dependencies
23159                 function sort(changes) {
23160
23161                     // find a referenced relation in the current changeset
23162                     function resolve(item) {
23163                         return relations.find(function(relation) {
23164                             return item.keyAttributes.type === 'relation'
23165                                 && item.keyAttributes.ref === relation['@id'];
23166                         });
23167                     }
23168
23169                     // a new item is an item that has not been already processed
23170                     function isNew(item) {
23171                         return !sorted[ item['@id'] ] && !processing.find(function(proc) {
23172                             return proc['@id'] === item['@id'];
23173                         });
23174                     }
23175
23176                     var processing = [];
23177                     var sorted = {};
23178                     var relations = changes.relation;
23179
23180                     if (!relations) return changes;
23181
23182                     for (var i = 0; i < relations.length; i++) {
23183                         var relation = relations[i];
23184
23185                         // skip relation if already sorted
23186                         if (!sorted[relation['@id']]) {
23187                             processing.push(relation);
23188                         }
23189
23190                         while (processing.length > 0) {
23191                             var next = processing[0],
23192                             deps = next.member.map(resolve).filter(Boolean).filter(isNew);
23193                             if (deps.length === 0) {
23194                                 sorted[next['@id']] = next;
23195                                 processing.shift();
23196                             } else {
23197                                 processing = deps.concat(processing);
23198                             }
23199                         }
23200                     }
23201
23202                     changes.relation = Object.values(sorted);
23203                     return changes;
23204                 }
23205
23206                 function rep(entity) {
23207                     return entity.asJXON(changeset_id);
23208                 }
23209
23210                 return {
23211                     osmChange: {
23212                         '@version': 0.6,
23213                         '@generator': 'iD',
23214                         'create': sort(nest(changes.created.map(rep), ['node', 'way', 'relation'])),
23215                         'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
23216                         'delete': Object.assign(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), { '@if-unused': true })
23217                     }
23218                 };
23219             },
23220
23221
23222             asGeoJSON: function() {
23223                 return {};
23224             }
23225
23226         });
23227
23228         function osmNote() {
23229             if (!(this instanceof osmNote)) {
23230                 return (new osmNote()).initialize(arguments);
23231             } else if (arguments.length) {
23232                 this.initialize(arguments);
23233             }
23234         }
23235
23236
23237         osmNote.id = function() {
23238             return osmNote.id.next--;
23239         };
23240
23241
23242         osmNote.id.next = -1;
23243
23244
23245         Object.assign(osmNote.prototype, {
23246
23247             type: 'note',
23248
23249             initialize: function(sources) {
23250                 for (var i = 0; i < sources.length; ++i) {
23251                     var source = sources[i];
23252                     for (var prop in source) {
23253                         if (Object.prototype.hasOwnProperty.call(source, prop)) {
23254                             if (source[prop] === undefined) {
23255                                 delete this[prop];
23256                             } else {
23257                                 this[prop] = source[prop];
23258                             }
23259                         }
23260                     }
23261                 }
23262
23263                 if (!this.id) {
23264                     this.id = osmNote.id() + '';  // as string
23265                 }
23266
23267                 return this;
23268             },
23269
23270             extent: function() {
23271                 return new geoExtent(this.loc);
23272             },
23273
23274             update: function(attrs) {
23275                 return osmNote(this, attrs); // {v: 1 + (this.v || 0)}
23276             },
23277
23278             isNew: function() {
23279                 return this.id < 0;
23280             },
23281
23282             move: function(loc) {
23283                 return this.update({ loc: loc });
23284             }
23285
23286         });
23287
23288         function osmRelation() {
23289             if (!(this instanceof osmRelation)) {
23290                 return (new osmRelation()).initialize(arguments);
23291             } else if (arguments.length) {
23292                 this.initialize(arguments);
23293             }
23294         }
23295
23296
23297         osmEntity.relation = osmRelation;
23298
23299         osmRelation.prototype = Object.create(osmEntity.prototype);
23300
23301
23302         osmRelation.creationOrder = function(a, b) {
23303             var aId = parseInt(osmEntity.id.toOSM(a.id), 10);
23304             var bId = parseInt(osmEntity.id.toOSM(b.id), 10);
23305
23306             if (aId < 0 || bId < 0) return aId - bId;
23307             return bId - aId;
23308         };
23309
23310
23311         Object.assign(osmRelation.prototype, {
23312             type: 'relation',
23313             members: [],
23314
23315
23316             copy: function(resolver, copies) {
23317                 if (copies[this.id]) return copies[this.id];
23318
23319                 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
23320
23321                 var members = this.members.map(function(member) {
23322                     return Object.assign({}, member, { id: resolver.entity(member.id).copy(resolver, copies).id });
23323                 });
23324
23325                 copy = copy.update({members: members});
23326                 copies[this.id] = copy;
23327
23328                 return copy;
23329             },
23330
23331
23332             extent: function(resolver, memo) {
23333                 return resolver.transient(this, 'extent', function() {
23334                     if (memo && memo[this.id]) return geoExtent();
23335                     memo = memo || {};
23336                     memo[this.id] = true;
23337
23338                     var extent = geoExtent();
23339                     for (var i = 0; i < this.members.length; i++) {
23340                         var member = resolver.hasEntity(this.members[i].id);
23341                         if (member) {
23342                             extent._extend(member.extent(resolver, memo));
23343                         }
23344                     }
23345                     return extent;
23346                 });
23347             },
23348
23349
23350             geometry: function(graph) {
23351                 return graph.transient(this, 'geometry', function() {
23352                     return this.isMultipolygon() ? 'area' : 'relation';
23353                 });
23354             },
23355
23356
23357             isDegenerate: function() {
23358                 return this.members.length === 0;
23359             },
23360
23361
23362             // Return an array of members, each extended with an 'index' property whose value
23363             // is the member index.
23364             indexedMembers: function() {
23365                 var result = new Array(this.members.length);
23366                 for (var i = 0; i < this.members.length; i++) {
23367                     result[i] = Object.assign({}, this.members[i], {index: i});
23368                 }
23369                 return result;
23370             },
23371
23372
23373             // Return the first member with the given role. A copy of the member object
23374             // is returned, extended with an 'index' property whose value is the member index.
23375             memberByRole: function(role) {
23376                 for (var i = 0; i < this.members.length; i++) {
23377                     if (this.members[i].role === role) {
23378                         return Object.assign({}, this.members[i], {index: i});
23379                     }
23380                 }
23381             },
23382
23383             // Same as memberByRole, but returns all members with the given role
23384             membersByRole: function(role) {
23385                 var result = [];
23386                 for (var i = 0; i < this.members.length; i++) {
23387                     if (this.members[i].role === role) {
23388                         result.push(Object.assign({}, this.members[i], {index: i}));
23389                     }
23390                 }
23391                 return result;
23392             },
23393
23394             // Return the first member with the given id. A copy of the member object
23395             // is returned, extended with an 'index' property whose value is the member index.
23396             memberById: function(id) {
23397                 for (var i = 0; i < this.members.length; i++) {
23398                     if (this.members[i].id === id) {
23399                         return Object.assign({}, this.members[i], {index: i});
23400                     }
23401                 }
23402             },
23403
23404
23405             // Return the first member with the given id and role. A copy of the member object
23406             // is returned, extended with an 'index' property whose value is the member index.
23407             memberByIdAndRole: function(id, role) {
23408                 for (var i = 0; i < this.members.length; i++) {
23409                     if (this.members[i].id === id && this.members[i].role === role) {
23410                         return Object.assign({}, this.members[i], {index: i});
23411                     }
23412                 }
23413             },
23414
23415
23416             addMember: function(member, index) {
23417                 var members = this.members.slice();
23418                 members.splice(index === undefined ? members.length : index, 0, member);
23419                 return this.update({members: members});
23420             },
23421
23422
23423             updateMember: function(member, index) {
23424                 var members = this.members.slice();
23425                 members.splice(index, 1, Object.assign({}, members[index], member));
23426                 return this.update({members: members});
23427             },
23428
23429
23430             removeMember: function(index) {
23431                 var members = this.members.slice();
23432                 members.splice(index, 1);
23433                 return this.update({members: members});
23434             },
23435
23436
23437             removeMembersWithID: function(id) {
23438                 var members = this.members.filter(function(m) { return m.id !== id; });
23439                 return this.update({members: members});
23440             },
23441
23442             moveMember: function(fromIndex, toIndex) {
23443                 var members = this.members.slice();
23444                 members.splice(toIndex, 0, members.splice(fromIndex, 1)[0]);
23445                 return this.update({members: members});
23446             },
23447
23448
23449             // Wherever a member appears with id `needle.id`, replace it with a member
23450             // with id `replacement.id`, type `replacement.type`, and the original role,
23451             // By default, adding a duplicate member (by id and role) is prevented.
23452             // Return an updated relation.
23453             replaceMember: function(needle, replacement, keepDuplicates) {
23454                 if (!this.memberById(needle.id)) return this;
23455
23456                 var members = [];
23457
23458                 for (var i = 0; i < this.members.length; i++) {
23459                     var member = this.members[i];
23460                     if (member.id !== needle.id) {
23461                         members.push(member);
23462                     } else if (keepDuplicates || !this.memberByIdAndRole(replacement.id, member.role)) {
23463                         members.push({ id: replacement.id, type: replacement.type, role: member.role });
23464                     }
23465                 }
23466
23467                 return this.update({ members: members });
23468             },
23469
23470
23471             asJXON: function(changeset_id) {
23472                 var r = {
23473                     relation: {
23474                         '@id': this.osmId(),
23475                         '@version': this.version || 0,
23476                         member: this.members.map(function(member) {
23477                             return {
23478                                 keyAttributes: {
23479                                     type: member.type,
23480                                     role: member.role,
23481                                     ref: osmEntity.id.toOSM(member.id)
23482                                 }
23483                             };
23484                         }, this),
23485                         tag: Object.keys(this.tags).map(function(k) {
23486                             return { keyAttributes: { k: k, v: this.tags[k] } };
23487                         }, this)
23488                     }
23489                 };
23490                 if (changeset_id) {
23491                     r.relation['@changeset'] = changeset_id;
23492                 }
23493                 return r;
23494             },
23495
23496
23497             asGeoJSON: function(resolver) {
23498                 return resolver.transient(this, 'GeoJSON', function () {
23499                     if (this.isMultipolygon()) {
23500                         return {
23501                             type: 'MultiPolygon',
23502                             coordinates: this.multipolygon(resolver)
23503                         };
23504                     } else {
23505                         return {
23506                             type: 'FeatureCollection',
23507                             properties: this.tags,
23508                             features: this.members.map(function (member) {
23509                                 return Object.assign({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver));
23510                             })
23511                         };
23512                     }
23513                 });
23514             },
23515
23516
23517             area: function(resolver) {
23518                 return resolver.transient(this, 'area', function() {
23519                     return d3_geoArea(this.asGeoJSON(resolver));
23520                 });
23521             },
23522
23523
23524             isMultipolygon: function() {
23525                 return this.tags.type === 'multipolygon';
23526             },
23527
23528
23529             isComplete: function(resolver) {
23530                 for (var i = 0; i < this.members.length; i++) {
23531                     if (!resolver.hasEntity(this.members[i].id)) {
23532                         return false;
23533                     }
23534                 }
23535                 return true;
23536             },
23537
23538
23539             hasFromViaTo: function() {
23540                 return (
23541                     this.members.some(function(m) { return m.role === 'from'; }) &&
23542                     this.members.some(function(m) { return m.role === 'via'; }) &&
23543                     this.members.some(function(m) { return m.role === 'to'; })
23544                 );
23545             },
23546
23547
23548             isRestriction: function() {
23549                 return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
23550             },
23551
23552
23553             isValidRestriction: function() {
23554                 if (!this.isRestriction()) return false;
23555
23556                 var froms = this.members.filter(function(m) { return m.role === 'from'; });
23557                 var vias = this.members.filter(function(m) { return m.role === 'via'; });
23558                 var tos = this.members.filter(function(m) { return m.role === 'to'; });
23559
23560                 if (froms.length !== 1 && this.tags.restriction !== 'no_entry') return false;
23561                 if (froms.some(function(m) { return m.type !== 'way'; })) return false;
23562
23563                 if (tos.length !== 1 && this.tags.restriction !== 'no_exit') return false;
23564                 if (tos.some(function(m) { return m.type !== 'way'; })) return false;
23565
23566                 if (vias.length === 0) return false;
23567                 if (vias.length > 1 && vias.some(function(m) { return m.type !== 'way'; })) return false;
23568
23569                 return true;
23570             },
23571
23572
23573             // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
23574             // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
23575             //
23576             // This corresponds to the structure needed for rendering a multipolygon path using a
23577             // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
23578             //
23579             // In the case of invalid geometries, this function will still return a result which
23580             // includes the nodes of all way members, but some Nds may be unclosed and some inner
23581             // rings not matched with the intended outer ring.
23582             //
23583             multipolygon: function(resolver) {
23584                 var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); });
23585                 var inners = this.members.filter(function(m) { return 'inner' === m.role; });
23586
23587                 outers = osmJoinWays(outers, resolver);
23588                 inners = osmJoinWays(inners, resolver);
23589
23590                 var sequenceToLineString = function(sequence) {
23591                     if (sequence.nodes.length > 2 &&
23592                         sequence.nodes[0] !== sequence.nodes[sequence.nodes.length - 1]) {
23593                         // close unclosed parts to ensure correct area rendering - #2945
23594                         sequence.nodes.push(sequence.nodes[0]);
23595                     }
23596                     return sequence.nodes.map(function(node) { return node.loc; });
23597                 };
23598
23599                 outers = outers.map(sequenceToLineString);
23600                 inners = inners.map(sequenceToLineString);
23601
23602                 var result = outers.map(function(o) {
23603                     // Heuristic for detecting counterclockwise winding order. Assumes
23604                     // that OpenStreetMap polygons are not hemisphere-spanning.
23605                     return [d3_geoArea({ type: 'Polygon', coordinates: [o] }) > 2 * Math.PI ? o.reverse() : o];
23606                 });
23607
23608                 function findOuter(inner) {
23609                     var o, outer;
23610
23611                     for (o = 0; o < outers.length; o++) {
23612                         outer = outers[o];
23613                         if (geoPolygonContainsPolygon(outer, inner))
23614                             return o;
23615                     }
23616
23617                     for (o = 0; o < outers.length; o++) {
23618                         outer = outers[o];
23619                         if (geoPolygonIntersectsPolygon(outer, inner, false))
23620                             return o;
23621                     }
23622                 }
23623
23624                 for (var i = 0; i < inners.length; i++) {
23625                     var inner = inners[i];
23626
23627                     if (d3_geoArea({ type: 'Polygon', coordinates: [inner] }) < 2 * Math.PI) {
23628                         inner = inner.reverse();
23629                     }
23630
23631                     var o = findOuter(inners[i]);
23632                     if (o !== undefined) {
23633                         result[o].push(inners[i]);
23634                     } else {
23635                         result.push([inners[i]]); // Invalid geometry
23636                     }
23637                 }
23638
23639                 return result;
23640             }
23641         });
23642
23643         class QAItem {
23644           constructor(loc, service, itemType, id, props) {
23645             // Store required properties
23646             this.loc = loc;
23647             this.service = service.title;
23648             this.itemType = itemType;
23649
23650             // All issues must have an ID for selection, use generic if none specified
23651             this.id = id ? id : `${QAItem.id()}`;
23652
23653             this.update(props);
23654
23655             // Some QA services have marker icons to differentiate issues
23656             if (service && typeof service.getIcon === 'function') {
23657               this.icon = service.getIcon(itemType);
23658             }
23659
23660             return this;
23661           }
23662
23663           update(props) {
23664             // You can't override this inital information
23665             const { loc, service, itemType, id } = this;
23666
23667             Object.keys(props).forEach(prop => this[prop] = props[prop]);
23668
23669             this.loc = loc;
23670             this.service = service;
23671             this.itemType = itemType;
23672             this.id = id;
23673
23674             return this;
23675           }
23676
23677           // Generic handling for newly created QAItems
23678           static id() {
23679             return this.nextId--;
23680           }
23681         }
23682         QAItem.nextId = -1;
23683
23684         // Split a way at the given node.
23685         //
23686         // Optionally, split only the given ways, if multiple ways share
23687         // the given node.
23688         //
23689         // This is the inverse of `iD.actionJoin`.
23690         //
23691         // For testing convenience, accepts an ID to assign to the new way.
23692         // Normally, this will be undefined and the way will automatically
23693         // be assigned a new ID.
23694         //
23695         // Reference:
23696         //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
23697         //
23698         function actionSplit(nodeId, newWayIds) {
23699             var _wayIDs;
23700
23701             // The IDs of the ways actually created by running this action
23702             var createdWayIDs = [];
23703
23704             // If the way is closed, we need to search for a partner node
23705             // to split the way at.
23706             //
23707             // The following looks for a node that is both far away from
23708             // the initial node in terms of way segment length and nearby
23709             // in terms of beeline-distance. This assures that areas get
23710             // split on the most "natural" points (independent of the number
23711             // of nodes).
23712             // For example: bone-shaped areas get split across their waist
23713             // line, circles across the diameter.
23714             function splitArea(nodes, idxA, graph) {
23715                 var lengths = new Array(nodes.length);
23716                 var length;
23717                 var i;
23718                 var best = 0;
23719                 var idxB;
23720
23721                 function wrap(index) {
23722                     return utilWrap(index, nodes.length);
23723                 }
23724
23725                 function dist(nA, nB) {
23726                     var locA = graph.entity(nA).loc;
23727                     var locB = graph.entity(nB).loc;
23728                     var epsilon = 1e-6;
23729                     return (locA && locB) ? geoSphericalDistance(locA, locB) : epsilon;
23730                 }
23731
23732                 // calculate lengths
23733                 length = 0;
23734                 for (i = wrap(idxA + 1); i !== idxA; i = wrap(i + 1)) {
23735                     length += dist(nodes[i], nodes[wrap(i - 1)]);
23736                     lengths[i] = length;
23737                 }
23738
23739                 length = 0;
23740                 for (i = wrap(idxA - 1); i !== idxA; i = wrap(i - 1)) {
23741                     length += dist(nodes[i], nodes[wrap(i + 1)]);
23742                     if (length < lengths[i]) {
23743                         lengths[i] = length;
23744                     }
23745                 }
23746
23747                 // determine best opposite node to split
23748                 for (i = 0; i < nodes.length; i++) {
23749                     var cost = lengths[i] / dist(nodes[idxA], nodes[i]);
23750                     if (cost > best) {
23751                         idxB = i;
23752                         best = cost;
23753                     }
23754                 }
23755
23756                 return idxB;
23757             }
23758
23759
23760             function split(graph, wayA, newWayId) {
23761                 var wayB = osmWay({ id: newWayId, tags: wayA.tags });   // `wayB` is the NEW way
23762                 var origNodes = wayA.nodes.slice();
23763                 var nodesA;
23764                 var nodesB;
23765                 var isArea = wayA.isArea();
23766                 var isOuter = osmIsOldMultipolygonOuterMember(wayA, graph);
23767
23768                 if (wayA.isClosed()) {
23769                     var nodes = wayA.nodes.slice(0, -1);
23770                     var idxA = nodes.indexOf(nodeId);
23771                     var idxB = splitArea(nodes, idxA, graph);
23772
23773                     if (idxB < idxA) {
23774                         nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
23775                         nodesB = nodes.slice(idxB, idxA + 1);
23776                     } else {
23777                         nodesA = nodes.slice(idxA, idxB + 1);
23778                         nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
23779                     }
23780                 } else {
23781                     var idx = wayA.nodes.indexOf(nodeId, 1);
23782                     nodesA = wayA.nodes.slice(0, idx + 1);
23783                     nodesB = wayA.nodes.slice(idx);
23784                 }
23785
23786                 wayA = wayA.update({ nodes: nodesA });
23787                 wayB = wayB.update({ nodes: nodesB });
23788
23789                 graph = graph.replace(wayA);
23790                 graph = graph.replace(wayB);
23791
23792                 graph.parentRelations(wayA).forEach(function(relation) {
23793                     var member;
23794
23795                     // Turn restrictions - make sure:
23796                     // 1. Splitting a FROM/TO way - only `wayA` OR `wayB` remains in relation
23797                     //    (whichever one is connected to the VIA node/ways)
23798                     // 2. Splitting a VIA way - `wayB` remains in relation as a VIA way
23799                     if (relation.hasFromViaTo()) {
23800                         var f = relation.memberByRole('from');
23801                         var v = relation.membersByRole('via');
23802                         var t = relation.memberByRole('to');
23803                         var i;
23804
23805                         // 1. split a FROM/TO
23806                         if (f.id === wayA.id || t.id === wayA.id) {
23807                             var keepB = false;
23808                             if (v.length === 1 && v[0].type === 'node') {   // check via node
23809                                 keepB = wayB.contains(v[0].id);
23810                             } else {                                        // check via way(s)
23811                                 for (i = 0; i < v.length; i++) {
23812                                     if (v[i].type === 'way') {
23813                                         var wayVia = graph.hasEntity(v[i].id);
23814                                         if (wayVia && utilArrayIntersection(wayB.nodes, wayVia.nodes).length) {
23815                                             keepB = true;
23816                                             break;
23817                                         }
23818                                     }
23819                                 }
23820                             }
23821
23822                             if (keepB) {
23823                                 relation = relation.replaceMember(wayA, wayB);
23824                                 graph = graph.replace(relation);
23825                             }
23826
23827                         // 2. split a VIA
23828                         } else {
23829                             for (i = 0; i < v.length; i++) {
23830                                 if (v[i].type === 'way' && v[i].id === wayA.id) {
23831                                     member = {
23832                                         id: wayB.id,
23833                                         type: 'way',
23834                                         role: 'via'
23835                                     };
23836                                     graph = actionAddMember(relation.id, member, v[i].index + 1)(graph);
23837                                     break;
23838                                 }
23839                             }
23840                         }
23841
23842                     // All other relations (Routes, Multipolygons, etc):
23843                     // 1. Both `wayA` and `wayB` remain in the relation
23844                     // 2. But must be inserted as a pair (see `actionAddMember` for details)
23845                     } else {
23846                         if (relation === isOuter) {
23847                             graph = graph.replace(relation.mergeTags(wayA.tags));
23848                             graph = graph.replace(wayA.update({ tags: {} }));
23849                             graph = graph.replace(wayB.update({ tags: {} }));
23850                         }
23851
23852                         member = {
23853                             id: wayB.id,
23854                             type: 'way',
23855                             role: relation.memberById(wayA.id).role
23856                         };
23857
23858                         var insertPair = {
23859                             originalID: wayA.id,
23860                             insertedID: wayB.id,
23861                             nodes: origNodes
23862                         };
23863
23864                         graph = actionAddMember(relation.id, member, undefined, insertPair)(graph);
23865                     }
23866                 });
23867
23868                 if (!isOuter && isArea) {
23869                     var multipolygon = osmRelation({
23870                         tags: Object.assign({}, wayA.tags, { type: 'multipolygon' }),
23871                         members: [
23872                             { id: wayA.id, role: 'outer', type: 'way' },
23873                             { id: wayB.id, role: 'outer', type: 'way' }
23874                         ]
23875                     });
23876
23877                     graph = graph.replace(multipolygon);
23878                     graph = graph.replace(wayA.update({ tags: {} }));
23879                     graph = graph.replace(wayB.update({ tags: {} }));
23880                 }
23881
23882                 createdWayIDs.push(wayB.id);
23883
23884                 return graph;
23885             }
23886
23887             var action = function(graph) {
23888                 var candidates = action.ways(graph);
23889                 createdWayIDs = [];
23890                 for (var i = 0; i < candidates.length; i++) {
23891                     graph = split(graph, candidates[i], newWayIds && newWayIds[i]);
23892                 }
23893                 return graph;
23894             };
23895
23896             action.getCreatedWayIDs = function() {
23897                 return createdWayIDs;
23898             };
23899
23900             action.ways = function(graph) {
23901                 var node = graph.entity(nodeId);
23902                 var parents = graph.parentWays(node);
23903                 var hasLines = parents.some(function(parent) {
23904                     return parent.geometry(graph) === 'line';
23905                 });
23906
23907                 return parents.filter(function(parent) {
23908                     if (_wayIDs && _wayIDs.indexOf(parent.id) === -1)
23909                         return false;
23910
23911                     if (!_wayIDs && hasLines && parent.geometry(graph) !== 'line')
23912                         return false;
23913
23914                     if (parent.isClosed()) {
23915                         return true;
23916                     }
23917
23918                     for (var i = 1; i < parent.nodes.length - 1; i++) {
23919                         if (parent.nodes[i] === nodeId) {
23920                             return true;
23921                         }
23922                     }
23923
23924                     return false;
23925                 });
23926             };
23927
23928
23929             action.disabled = function(graph) {
23930                 var candidates = action.ways(graph);
23931                 if (candidates.length === 0 || (_wayIDs && _wayIDs.length !== candidates.length)) {
23932                     return 'not_eligible';
23933                 }
23934             };
23935
23936
23937             action.limitWays = function(val) {
23938                 if (!arguments.length) return _wayIDs;
23939                 _wayIDs = val;
23940                 return action;
23941             };
23942
23943
23944             return action;
23945         }
23946
23947         function coreGraph(other, mutable) {
23948             if (!(this instanceof coreGraph)) return new coreGraph(other, mutable);
23949
23950             if (other instanceof coreGraph) {
23951                 var base = other.base();
23952                 this.entities = Object.assign(Object.create(base.entities), other.entities);
23953                 this._parentWays = Object.assign(Object.create(base.parentWays), other._parentWays);
23954                 this._parentRels = Object.assign(Object.create(base.parentRels), other._parentRels);
23955
23956             } else {
23957                 this.entities = Object.create({});
23958                 this._parentWays = Object.create({});
23959                 this._parentRels = Object.create({});
23960                 this.rebase(other || [], [this]);
23961             }
23962
23963             this.transients = {};
23964             this._childNodes = {};
23965             this.frozen = !mutable;
23966         }
23967
23968
23969         coreGraph.prototype = {
23970
23971             hasEntity: function(id) {
23972                 return this.entities[id];
23973             },
23974
23975
23976             entity: function(id) {
23977                 var entity = this.entities[id];
23978
23979                 //https://github.com/openstreetmap/iD/issues/3973#issuecomment-307052376
23980                 if (!entity) {
23981                     entity = this.entities.__proto__[id];  // eslint-disable-line no-proto
23982                 }
23983
23984                 if (!entity) {
23985                     throw new Error('entity ' + id + ' not found');
23986                 }
23987                 return entity;
23988             },
23989
23990
23991             geometry: function(id) {
23992                 return this.entity(id).geometry(this);
23993             },
23994
23995
23996             transient: function(entity, key, fn) {
23997                 var id = entity.id;
23998                 var transients = this.transients[id] || (this.transients[id] = {});
23999
24000                 if (transients[key] !== undefined) {
24001                     return transients[key];
24002                 }
24003
24004                 transients[key] = fn.call(entity);
24005
24006                 return transients[key];
24007             },
24008
24009
24010             parentWays: function(entity) {
24011                 var parents = this._parentWays[entity.id];
24012                 var result = [];
24013                 if (parents) {
24014                     parents.forEach(function(id) {
24015                         result.push(this.entity(id));
24016                     }, this);
24017                 }
24018                 return result;
24019             },
24020
24021
24022             isPoi: function(entity) {
24023                 var parents = this._parentWays[entity.id];
24024                 return !parents || parents.size === 0;
24025             },
24026
24027
24028             isShared: function(entity) {
24029                 var parents = this._parentWays[entity.id];
24030                 return parents && parents.size > 1;
24031             },
24032
24033
24034             parentRelations: function(entity) {
24035                 var parents = this._parentRels[entity.id];
24036                 var result = [];
24037                 if (parents) {
24038                     parents.forEach(function(id) {
24039                         result.push(this.entity(id));
24040                     }, this);
24041                 }
24042                 return result;
24043             },
24044
24045             parentMultipolygons: function(entity) {
24046                 return this.parentRelations(entity).filter(function(relation) {
24047                     return relation.isMultipolygon();
24048                 });
24049             },
24050
24051
24052             childNodes: function(entity) {
24053                 if (this._childNodes[entity.id]) return this._childNodes[entity.id];
24054                 if (!entity.nodes) return [];
24055
24056                 var nodes = [];
24057                 for (var i = 0; i < entity.nodes.length; i++) {
24058                     nodes[i] = this.entity(entity.nodes[i]);
24059                 }
24060
24061                 this._childNodes[entity.id] = nodes;
24062                 return this._childNodes[entity.id];
24063             },
24064
24065
24066             base: function() {
24067                 return {
24068                     'entities': Object.getPrototypeOf(this.entities),
24069                     'parentWays': Object.getPrototypeOf(this._parentWays),
24070                     'parentRels': Object.getPrototypeOf(this._parentRels)
24071                 };
24072             },
24073
24074
24075             // Unlike other graph methods, rebase mutates in place. This is because it
24076             // is used only during the history operation that merges newly downloaded
24077             // data into each state. To external consumers, it should appear as if the
24078             // graph always contained the newly downloaded data.
24079             rebase: function(entities, stack, force) {
24080                 var base = this.base();
24081                 var i, j, k, id;
24082
24083                 for (i = 0; i < entities.length; i++) {
24084                     var entity = entities[i];
24085
24086                     if (!entity.visible || (!force && base.entities[entity.id]))
24087                         continue;
24088
24089                     // Merging data into the base graph
24090                     base.entities[entity.id] = entity;
24091                     this._updateCalculated(undefined, entity, base.parentWays, base.parentRels);
24092
24093                     // Restore provisionally-deleted nodes that are discovered to have an extant parent
24094                     if (entity.type === 'way') {
24095                         for (j = 0; j < entity.nodes.length; j++) {
24096                             id = entity.nodes[j];
24097                             for (k = 1; k < stack.length; k++) {
24098                                 var ents = stack[k].entities;
24099                                 if (ents.hasOwnProperty(id) && ents[id] === undefined) {
24100                                     delete ents[id];
24101                                 }
24102                             }
24103                         }
24104                     }
24105                 }
24106
24107                 for (i = 0; i < stack.length; i++) {
24108                     stack[i]._updateRebased();
24109                 }
24110             },
24111
24112
24113             _updateRebased: function() {
24114                 var base = this.base();
24115
24116                 Object.keys(this._parentWays).forEach(function(child) {
24117                     if (base.parentWays[child]) {
24118                         base.parentWays[child].forEach(function(id) {
24119                             if (!this.entities.hasOwnProperty(id)) {
24120                                 this._parentWays[child].add(id);
24121                             }
24122                         }, this);
24123                     }
24124                 }, this);
24125
24126                 Object.keys(this._parentRels).forEach(function(child) {
24127                     if (base.parentRels[child]) {
24128                         base.parentRels[child].forEach(function(id) {
24129                             if (!this.entities.hasOwnProperty(id)) {
24130                                 this._parentRels[child].add(id);
24131                             }
24132                         }, this);
24133                     }
24134                 }, this);
24135
24136                 this.transients = {};
24137
24138                 // this._childNodes is not updated, under the assumption that
24139                 // ways are always downloaded with their child nodes.
24140             },
24141
24142
24143             // Updates calculated properties (parentWays, parentRels) for the specified change
24144             _updateCalculated: function(oldentity, entity, parentWays, parentRels) {
24145                 parentWays = parentWays || this._parentWays;
24146                 parentRels = parentRels || this._parentRels;
24147
24148                 var type = entity && entity.type || oldentity && oldentity.type;
24149                 var removed, added, i;
24150
24151                 if (type === 'way') {   // Update parentWays
24152                     if (oldentity && entity) {
24153                         removed = utilArrayDifference(oldentity.nodes, entity.nodes);
24154                         added = utilArrayDifference(entity.nodes, oldentity.nodes);
24155                     } else if (oldentity) {
24156                         removed = oldentity.nodes;
24157                         added = [];
24158                     } else if (entity) {
24159                         removed = [];
24160                         added = entity.nodes;
24161                     }
24162                     for (i = 0; i < removed.length; i++) {
24163                         // make a copy of prototype property, store as own property, and update..
24164                         parentWays[removed[i]] = new Set(parentWays[removed[i]]);
24165                         parentWays[removed[i]].delete(oldentity.id);
24166                     }
24167                     for (i = 0; i < added.length; i++) {
24168                         // make a copy of prototype property, store as own property, and update..
24169                         parentWays[added[i]] = new Set(parentWays[added[i]]);
24170                         parentWays[added[i]].add(entity.id);
24171                     }
24172
24173                 } else if (type === 'relation') {   // Update parentRels
24174
24175                     // diff only on the IDs since the same entity can be a member multiple times with different roles
24176                     var oldentityMemberIDs = oldentity ? oldentity.members.map(function(m) { return m.id; }) : [];
24177                     var entityMemberIDs = entity ? entity.members.map(function(m) { return m.id; }) : [];
24178
24179                     if (oldentity && entity) {
24180                         removed = utilArrayDifference(oldentityMemberIDs, entityMemberIDs);
24181                         added = utilArrayDifference(entityMemberIDs, oldentityMemberIDs);
24182                     } else if (oldentity) {
24183                         removed = oldentityMemberIDs;
24184                         added = [];
24185                     } else if (entity) {
24186                         removed = [];
24187                         added = entityMemberIDs;
24188                     }
24189                     for (i = 0; i < removed.length; i++) {
24190                         // make a copy of prototype property, store as own property, and update..
24191                         parentRels[removed[i]] = new Set(parentRels[removed[i]]);
24192                         parentRels[removed[i]].delete(oldentity.id);
24193                     }
24194                     for (i = 0; i < added.length; i++) {
24195                         // make a copy of prototype property, store as own property, and update..
24196                         parentRels[added[i]] = new Set(parentRels[added[i]]);
24197                         parentRels[added[i]].add(entity.id);
24198                     }
24199                 }
24200             },
24201
24202
24203             replace: function(entity) {
24204                 if (this.entities[entity.id] === entity) return this;
24205
24206                 return this.update(function() {
24207                     this._updateCalculated(this.entities[entity.id], entity);
24208                     this.entities[entity.id] = entity;
24209                 });
24210             },
24211
24212
24213             remove: function(entity) {
24214                 return this.update(function() {
24215                     this._updateCalculated(entity, undefined);
24216                     this.entities[entity.id] = undefined;
24217                 });
24218             },
24219
24220
24221             revert: function(id) {
24222                 var baseEntity = this.base().entities[id];
24223                 var headEntity = this.entities[id];
24224                 if (headEntity === baseEntity) return this;
24225
24226                 return this.update(function() {
24227                     this._updateCalculated(headEntity, baseEntity);
24228                     delete this.entities[id];
24229                 });
24230             },
24231
24232
24233             update: function() {
24234                 var graph = this.frozen ? coreGraph(this, true) : this;
24235                 for (var i = 0; i < arguments.length; i++) {
24236                     arguments[i].call(graph, graph);
24237                 }
24238
24239                 if (this.frozen) graph.frozen = true;
24240
24241                 return graph;
24242             },
24243
24244
24245             // Obliterates any existing entities
24246             load: function(entities) {
24247                 var base = this.base();
24248                 this.entities = Object.create(base.entities);
24249
24250                 for (var i in entities) {
24251                     this.entities[i] = entities[i];
24252                     this._updateCalculated(base.entities[i], this.entities[i]);
24253                 }
24254
24255                 return this;
24256             }
24257         };
24258
24259         function osmTurn(turn) {
24260             if (!(this instanceof osmTurn)) {
24261                 return new osmTurn(turn);
24262             }
24263             Object.assign(this, turn);
24264         }
24265
24266
24267         function osmIntersection(graph, startVertexId, maxDistance) {
24268             maxDistance = maxDistance || 30;    // in meters
24269             var vgraph = coreGraph();           // virtual graph
24270             var i, j, k;
24271
24272
24273             function memberOfRestriction(entity) {
24274                 return graph.parentRelations(entity)
24275                     .some(function(r) { return r.isRestriction(); });
24276             }
24277
24278             function isRoad(way) {
24279                 if (way.isArea() || way.isDegenerate()) return false;
24280                 var roads = {
24281                     'motorway': true,
24282                     'motorway_link': true,
24283                     'trunk': true,
24284                     'trunk_link': true,
24285                     'primary': true,
24286                     'primary_link': true,
24287                     'secondary': true,
24288                     'secondary_link': true,
24289                     'tertiary': true,
24290                     'tertiary_link': true,
24291                     'residential': true,
24292                     'unclassified': true,
24293                     'living_street': true,
24294                     'service': true,
24295                     'road': true,
24296                     'track': true
24297                 };
24298                 return roads[way.tags.highway];
24299             }
24300
24301
24302             var startNode = graph.entity(startVertexId);
24303             var checkVertices = [startNode];
24304             var checkWays;
24305             var vertices = [];
24306             var vertexIds = [];
24307             var vertex;
24308             var ways = [];
24309             var wayIds = [];
24310             var way;
24311             var nodes = [];
24312             var node;
24313             var parents = [];
24314             var parent;
24315
24316             // `actions` will store whatever actions must be performed to satisfy
24317             // preconditions for adding a turn restriction to this intersection.
24318             //  - Remove any existing degenerate turn restrictions (missing from/to, etc)
24319             //  - Reverse oneways so that they are drawn in the forward direction
24320             //  - Split ways on key vertices
24321             var actions = [];
24322
24323
24324             // STEP 1:  walk the graph outwards from starting vertex to search
24325             //  for more key vertices and ways to include in the intersection..
24326
24327             while (checkVertices.length) {
24328                 vertex = checkVertices.pop();
24329
24330                 // check this vertex for parent ways that are roads
24331                 checkWays = graph.parentWays(vertex);
24332                 var hasWays = false;
24333                 for (i = 0; i < checkWays.length; i++) {
24334                     way = checkWays[i];
24335                     if (!isRoad(way) && !memberOfRestriction(way)) continue;
24336
24337                     ways.push(way);   // it's a road, or it's already in a turn restriction
24338                     hasWays = true;
24339
24340                     // check the way's children for more key vertices
24341                     nodes = utilArrayUniq(graph.childNodes(way));
24342                     for (j = 0; j < nodes.length; j++) {
24343                         node = nodes[j];
24344                         if (node === vertex) continue;                                           // same thing
24345                         if (vertices.indexOf(node) !== -1) continue;                             // seen it already
24346                         if (geoSphericalDistance(node.loc, startNode.loc) > maxDistance) continue;   // too far from start
24347
24348                         // a key vertex will have parents that are also roads
24349                         var hasParents = false;
24350                         parents = graph.parentWays(node);
24351                         for (k = 0; k < parents.length; k++) {
24352                             parent = parents[k];
24353                             if (parent === way) continue;                 // same thing
24354                             if (ways.indexOf(parent) !== -1) continue;    // seen it already
24355                             if (!isRoad(parent)) continue;                // not a road
24356                             hasParents = true;
24357                             break;
24358                         }
24359
24360                         if (hasParents) {
24361                             checkVertices.push(node);
24362                         }
24363                     }
24364                 }
24365
24366                 if (hasWays) {
24367                     vertices.push(vertex);
24368                 }
24369             }
24370
24371             vertices = utilArrayUniq(vertices);
24372             ways = utilArrayUniq(ways);
24373
24374
24375             // STEP 2:  Build a virtual graph containing only the entities in the intersection..
24376             // Everything done after this step should act on the virtual graph
24377             // Any actions that must be performed later to the main graph go in `actions` array
24378             ways.forEach(function(way) {
24379                 graph.childNodes(way).forEach(function(node) {
24380                     vgraph = vgraph.replace(node);
24381                 });
24382
24383                 vgraph = vgraph.replace(way);
24384
24385                 graph.parentRelations(way).forEach(function(relation) {
24386                     if (relation.isRestriction()) {
24387                         if (relation.isValidRestriction(graph)) {
24388                             vgraph = vgraph.replace(relation);
24389                         } else if (relation.isComplete(graph)) {
24390                             actions.push(actionDeleteRelation(relation.id));
24391                         }
24392                     }
24393                 });
24394             });
24395
24396
24397             // STEP 3:  Force all oneways to be drawn in the forward direction
24398             ways.forEach(function(w) {
24399                 var way = vgraph.entity(w.id);
24400                 if (way.tags.oneway === '-1') {
24401                     var action = actionReverse(way.id, { reverseOneway: true });
24402                     actions.push(action);
24403                     vgraph = action(vgraph);
24404                 }
24405             });
24406
24407
24408             // STEP 4:  Split ways on key vertices
24409             var origCount = osmEntity.id.next.way;
24410             vertices.forEach(function(v) {
24411                 // This is an odd way to do it, but we need to find all the ways that
24412                 // will be split here, then split them one at a time to ensure that these
24413                 // actions can be replayed on the main graph exactly in the same order.
24414                 // (It is unintuitive, but the order of ways returned from graph.parentWays()
24415                 // is arbitrary, depending on how the main graph and vgraph were built)
24416                 var splitAll = actionSplit(v.id);
24417                 if (!splitAll.disabled(vgraph)) {
24418                     splitAll.ways(vgraph).forEach(function(way) {
24419                         var splitOne = actionSplit(v.id).limitWays([way.id]);
24420                         actions.push(splitOne);
24421                         vgraph = splitOne(vgraph);
24422                     });
24423                 }
24424             });
24425
24426             // In here is where we should also split the intersection at nearby junction.
24427             //   for https://github.com/mapbox/iD-internal/issues/31
24428             // nearbyVertices.forEach(function(v) {
24429             // });
24430
24431             // Reasons why we reset the way id count here:
24432             //  1. Continuity with way ids created by the splits so that we can replay
24433             //     these actions later if the user decides to create a turn restriction
24434             //  2. Avoids churning way ids just by hovering over a vertex
24435             //     and displaying the turn restriction editor
24436             osmEntity.id.next.way = origCount;
24437
24438
24439             // STEP 5:  Update arrays to point to vgraph entities
24440             vertexIds = vertices.map(function(v) { return v.id; });
24441             vertices = [];
24442             ways = [];
24443
24444             vertexIds.forEach(function(id) {
24445                 var vertex = vgraph.entity(id);
24446                 var parents = vgraph.parentWays(vertex);
24447                 vertices.push(vertex);
24448                 ways = ways.concat(parents);
24449             });
24450
24451             vertices = utilArrayUniq(vertices);
24452             ways = utilArrayUniq(ways);
24453
24454             vertexIds = vertices.map(function(v) { return v.id; });
24455             wayIds = ways.map(function(w) { return w.id; });
24456
24457
24458             // STEP 6:  Update the ways with some metadata that will be useful for
24459             // walking the intersection graph later and rendering turn arrows.
24460
24461             function withMetadata(way, vertexIds) {
24462                 var __oneWay = way.isOneWay();
24463
24464                 // which affixes are key vertices?
24465                 var __first = (vertexIds.indexOf(way.first()) !== -1);
24466                 var __last = (vertexIds.indexOf(way.last()) !== -1);
24467
24468                 // what roles is this way eligible for?
24469                 var __via = (__first && __last);
24470                 var __from = ((__first && !__oneWay) || __last);
24471                 var __to = (__first || (__last && !__oneWay));
24472
24473                 return way.update({
24474                     __first:  __first,
24475                     __last:  __last,
24476                     __from:  __from,
24477                     __via: __via,
24478                     __to:  __to,
24479                     __oneWay:  __oneWay
24480                 });
24481             }
24482
24483             ways = [];
24484             wayIds.forEach(function(id) {
24485                 var way = withMetadata(vgraph.entity(id), vertexIds);
24486                 vgraph = vgraph.replace(way);
24487                 ways.push(way);
24488             });
24489
24490
24491             // STEP 7:  Simplify - This is an iterative process where we:
24492             //  1. Find trivial vertices with only 2 parents
24493             //  2. trim off the leaf way from those vertices and remove from vgraph
24494
24495             var keepGoing;
24496             var removeWayIds = [];
24497             var removeVertexIds = [];
24498
24499             do {
24500                 keepGoing = false;
24501                 checkVertices = vertexIds.slice();
24502
24503                 for (i = 0; i < checkVertices.length; i++) {
24504                     var vertexId = checkVertices[i];
24505                     vertex = vgraph.hasEntity(vertexId);
24506
24507                     if (!vertex) {
24508                         if (vertexIds.indexOf(vertexId) !== -1) {
24509                             vertexIds.splice(vertexIds.indexOf(vertexId), 1);   // stop checking this one
24510                         }
24511                         removeVertexIds.push(vertexId);
24512                         continue;
24513                     }
24514
24515                     parents = vgraph.parentWays(vertex);
24516                     if (parents.length < 3) {
24517                         if (vertexIds.indexOf(vertexId) !== -1) {
24518                             vertexIds.splice(vertexIds.indexOf(vertexId), 1);   // stop checking this one
24519                         }
24520                     }
24521
24522                     if (parents.length === 2) {     // vertex with 2 parents is trivial
24523                         var a = parents[0];
24524                         var b = parents[1];
24525                         var aIsLeaf = a && !a.__via;
24526                         var bIsLeaf = b && !b.__via;
24527                         var leaf, survivor;
24528
24529                         if (aIsLeaf && !bIsLeaf) {
24530                             leaf = a;
24531                             survivor = b;
24532                         } else if (!aIsLeaf && bIsLeaf) {
24533                             leaf = b;
24534                             survivor = a;
24535                         }
24536
24537                         if (leaf && survivor) {
24538                             survivor = withMetadata(survivor, vertexIds);      // update survivor way
24539                             vgraph = vgraph.replace(survivor).remove(leaf);    // update graph
24540                             removeWayIds.push(leaf.id);
24541                             keepGoing = true;
24542                         }
24543                     }
24544
24545                     parents = vgraph.parentWays(vertex);
24546
24547                     if (parents.length < 2) {     // vertex is no longer a key vertex
24548                         if (vertexIds.indexOf(vertexId) !== -1) {
24549                             vertexIds.splice(vertexIds.indexOf(vertexId), 1);   // stop checking this one
24550                         }
24551                         removeVertexIds.push(vertexId);
24552                         keepGoing = true;
24553                     }
24554
24555                     if (parents.length < 1) {     // vertex is no longer attached to anything
24556                         vgraph = vgraph.remove(vertex);
24557                     }
24558
24559                 }
24560             } while (keepGoing);
24561
24562
24563             vertices = vertices
24564                 .filter(function(vertex) { return removeVertexIds.indexOf(vertex.id) === -1; })
24565                 .map(function(vertex) { return vgraph.entity(vertex.id); });
24566             ways = ways
24567                 .filter(function(way) { return removeWayIds.indexOf(way.id) === -1; })
24568                 .map(function(way) { return vgraph.entity(way.id); });
24569
24570
24571             // OK!  Here is our intersection..
24572             var intersection = {
24573                 graph: vgraph,
24574                 actions: actions,
24575                 vertices: vertices,
24576                 ways: ways,
24577             };
24578
24579
24580
24581             // Get all the valid turns through this intersection given a starting way id.
24582             // This operates on the virtual graph for everything.
24583             //
24584             // Basically, walk through all possible paths from starting way,
24585             //   honoring the existing turn restrictions as we go (watch out for loops!)
24586             //
24587             // For each path found, generate and return a `osmTurn` datastructure.
24588             //
24589             intersection.turns = function(fromWayId, maxViaWay) {
24590                 if (!fromWayId) return [];
24591                 if (!maxViaWay) maxViaWay = 0;
24592
24593                 var vgraph = intersection.graph;
24594                 var keyVertexIds = intersection.vertices.map(function(v) { return v.id; });
24595
24596                 var start = vgraph.entity(fromWayId);
24597                 if (!start || !(start.__from || start.__via)) return [];
24598
24599                 // maxViaWay=0   from-*-to              (0 vias)
24600                 // maxViaWay=1   from-*-via-*-to        (1 via max)
24601                 // maxViaWay=2   from-*-via-*-via-*-to  (2 vias max)
24602                 var maxPathLength = (maxViaWay * 2) + 3;
24603                 var turns = [];
24604
24605                 step(start);
24606                 return turns;
24607
24608
24609                 // traverse the intersection graph and find all the valid paths
24610                 function step(entity, currPath, currRestrictions, matchedRestriction) {
24611                     currPath = (currPath || []).slice();  // shallow copy
24612                     if (currPath.length >= maxPathLength) return;
24613                     currPath.push(entity.id);
24614                     currRestrictions = (currRestrictions || []).slice();  // shallow copy
24615                     var i, j;
24616
24617                     if (entity.type === 'node') {
24618                         var parents = vgraph.parentWays(entity);
24619                         var nextWays = [];
24620
24621                         // which ways can we step into?
24622                         for (i = 0; i < parents.length; i++) {
24623                             var way = parents[i];
24624
24625                             // if next way is a oneway incoming to this vertex, skip
24626                             if (way.__oneWay && way.nodes[0] !== entity.id) continue;
24627
24628                             // if we have seen it before (allowing for an initial u-turn), skip
24629                             if (currPath.indexOf(way.id) !== -1 && currPath.length >= 3) continue;
24630
24631                             // Check all "current" restrictions (where we've already walked the `FROM`)
24632                             var restrict = undefined;
24633                             for (j = 0; j < currRestrictions.length; j++) {
24634                                 var restriction = currRestrictions[j];
24635                                 var f = restriction.memberByRole('from');
24636                                 var v = restriction.membersByRole('via');
24637                                 var t = restriction.memberByRole('to');
24638                                 var isOnly = /^only_/.test(restriction.tags.restriction);
24639
24640                                 // Does the current path match this turn restriction?
24641                                 var matchesFrom = (f.id === fromWayId);
24642                                 var matchesViaTo = false;
24643                                 var isAlongOnlyPath = false;
24644
24645                                 if (t.id === way.id) {     // match TO
24646
24647                                     if (v.length === 1 && v[0].type === 'node') {    // match VIA node
24648                                         matchesViaTo = (v[0].id === entity.id && (
24649                                             (matchesFrom && currPath.length === 2) ||
24650                                             (!matchesFrom && currPath.length > 2)
24651                                         ));
24652
24653                                     } else {                                         // match all VIA ways
24654                                         var pathVias = [];
24655                                         for (k = 2; k < currPath.length; k +=2 ) {   // k = 2 skips FROM
24656                                             pathVias.push(currPath[k]);              // (path goes way-node-way...)
24657                                         }
24658                                         var restrictionVias = [];
24659                                         for (k = 0; k < v.length; k++) {
24660                                             if (v[k].type === 'way') {
24661                                                 restrictionVias.push(v[k].id);
24662                                             }
24663                                         }
24664                                         var diff = utilArrayDifference(pathVias, restrictionVias);
24665                                         matchesViaTo = !diff.length;
24666                                     }
24667
24668                                 } else if (isOnly) {
24669                                     for (k = 0; k < v.length; k++) {
24670                                         // way doesn't match TO, but is one of the via ways along the path of an "only"
24671                                         if (v[k].type === 'way' && v[k].id === way.id) {
24672                                             isAlongOnlyPath = true;
24673                                             break;
24674                                         }
24675                                     }
24676                                 }
24677
24678                                 if (matchesViaTo) {
24679                                     if (isOnly) {
24680                                         restrict = { id: restriction.id, direct: matchesFrom, from: f.id, only: true, end: true };
24681                                     } else {
24682                                         restrict = { id: restriction.id, direct: matchesFrom, from: f.id, no: true, end: true };
24683                                     }
24684                                 } else {    // indirect - caused by a different nearby restriction
24685                                     if (isAlongOnlyPath) {
24686                                         restrict = { id: restriction.id, direct: false, from: f.id, only: true, end: false };
24687                                     } else if (isOnly) {
24688                                         restrict = { id: restriction.id, direct: false, from: f.id, no: true, end: true };
24689                                     }
24690                                 }
24691
24692                                 // stop looking if we find a "direct" restriction (matching FROM, VIA, TO)
24693                                 if (restrict && restrict.direct)
24694                                     break;
24695                             }
24696
24697                             nextWays.push({ way: way, restrict: restrict });
24698                         }
24699
24700                         nextWays.forEach(function(nextWay) {
24701                             step(nextWay.way, currPath, currRestrictions, nextWay.restrict);
24702                         });
24703
24704
24705                     } else {  // entity.type === 'way'
24706                         if (currPath.length >= 3) {     // this is a "complete" path..
24707                             var turnPath = currPath.slice();   // shallow copy
24708
24709                             // an indirect restriction - only include the partial path (starting at FROM)
24710                             if (matchedRestriction && matchedRestriction.direct === false) {
24711                                 for (i = 0; i < turnPath.length; i++) {
24712                                     if (turnPath[i] === matchedRestriction.from) {
24713                                         turnPath = turnPath.slice(i);
24714                                         break;
24715                                     }
24716                                 }
24717                             }
24718
24719                             var turn = pathToTurn(turnPath);
24720                             if (turn) {
24721                                 if (matchedRestriction) {
24722                                     turn.restrictionID = matchedRestriction.id;
24723                                     turn.no = matchedRestriction.no;
24724                                     turn.only = matchedRestriction.only;
24725                                     turn.direct = matchedRestriction.direct;
24726                                 }
24727                                 turns.push(osmTurn(turn));
24728                             }
24729
24730                             if (currPath[0] === currPath[2]) return;   // if we made a u-turn - stop here
24731                         }
24732
24733                         if (matchedRestriction && matchedRestriction.end) return;  // don't advance any further
24734
24735                         // which nodes can we step into?
24736                         var n1 = vgraph.entity(entity.first());
24737                         var n2 = vgraph.entity(entity.last());
24738                         var dist = geoSphericalDistance(n1.loc, n2.loc);
24739                         var nextNodes = [];
24740
24741                         if (currPath.length > 1) {
24742                             if (dist > maxDistance) return;   // the next node is too far
24743                             if (!entity.__via) return;        // this way is a leaf / can't be a via
24744                         }
24745
24746                         if (!entity.__oneWay &&                     // bidirectional..
24747                             keyVertexIds.indexOf(n1.id) !== -1 &&   // key vertex..
24748                             currPath.indexOf(n1.id) === -1) {       // haven't seen it yet..
24749                             nextNodes.push(n1);                     // can advance to first node
24750                         }
24751                         if (keyVertexIds.indexOf(n2.id) !== -1 &&   // key vertex..
24752                             currPath.indexOf(n2.id) === -1) {       // haven't seen it yet..
24753                             nextNodes.push(n2);                     // can advance to last node
24754                         }
24755
24756                         nextNodes.forEach(function(nextNode) {
24757                             // gather restrictions FROM this way
24758                             var fromRestrictions = vgraph.parentRelations(entity).filter(function(r) {
24759                                 if (!r.isRestriction()) return false;
24760
24761                                 var f = r.memberByRole('from');
24762                                 if (!f || f.id !== entity.id) return false;
24763
24764                                 var isOnly = /^only_/.test(r.tags.restriction);
24765                                 if (!isOnly) return true;
24766
24767                                 // `only_` restrictions only matter along the direction of the VIA - #4849
24768                                 var isOnlyVia = false;
24769                                 var v = r.membersByRole('via');
24770                                 if (v.length === 1 && v[0].type === 'node') {   // via node
24771                                     isOnlyVia = (v[0].id === nextNode.id);
24772                                 } else {                                        // via way(s)
24773                                     for (var i = 0; i < v.length; i++) {
24774                                         if (v[i].type !== 'way') continue;
24775                                         var viaWay = vgraph.entity(v[i].id);
24776                                         if (viaWay.first() === nextNode.id || viaWay.last() === nextNode.id) {
24777                                             isOnlyVia = true;
24778                                             break;
24779                                         }
24780                                     }
24781                                 }
24782                                 return isOnlyVia;
24783                             });
24784
24785                             step(nextNode, currPath, currRestrictions.concat(fromRestrictions), false);
24786                         });
24787                     }
24788                 }
24789
24790
24791                 // assumes path is alternating way-node-way of odd length
24792                 function pathToTurn(path) {
24793                     if (path.length < 3) return;
24794                     var fromWayId, fromNodeId, fromVertexId;
24795                     var toWayId, toNodeId, toVertexId;
24796                     var viaWayIds, viaNodeId, isUturn;
24797
24798                     fromWayId = path[0];
24799                     toWayId = path[path.length - 1];
24800
24801                     if (path.length === 3 && fromWayId === toWayId) {  // u turn
24802                         var way = vgraph.entity(fromWayId);
24803                         if (way.__oneWay) return null;
24804
24805                         isUturn = true;
24806                         viaNodeId = fromVertexId = toVertexId = path[1];
24807                         fromNodeId = toNodeId = adjacentNode(fromWayId, viaNodeId);
24808
24809                     } else {
24810                         isUturn = false;
24811                         fromVertexId = path[1];
24812                         fromNodeId = adjacentNode(fromWayId, fromVertexId);
24813                         toVertexId = path[path.length - 2];
24814                         toNodeId = adjacentNode(toWayId, toVertexId);
24815
24816                         if (path.length === 3) {
24817                             viaNodeId = path[1];
24818                         } else {
24819                             viaWayIds = path.filter(function(entityId) { return entityId[0] === 'w'; });
24820                             viaWayIds = viaWayIds.slice(1, viaWayIds.length - 1);  // remove first, last
24821                         }
24822                     }
24823
24824                     return {
24825                         key:  path.join('_'),
24826                         path: path,
24827                         from: { node: fromNodeId, way:  fromWayId, vertex: fromVertexId },
24828                         via:  { node: viaNodeId,  ways: viaWayIds },
24829                         to:   { node: toNodeId,   way:  toWayId, vertex: toVertexId },
24830                         u:    isUturn
24831                     };
24832
24833
24834                     function adjacentNode(wayId, affixId) {
24835                         var nodes = vgraph.entity(wayId).nodes;
24836                         return affixId === nodes[0] ? nodes[1] : nodes[nodes.length - 2];
24837                     }
24838                 }
24839
24840             };
24841
24842             return intersection;
24843         }
24844
24845
24846         function osmInferRestriction(graph, turn, projection) {
24847             var fromWay = graph.entity(turn.from.way);
24848             var fromNode = graph.entity(turn.from.node);
24849             var fromVertex = graph.entity(turn.from.vertex);
24850             var toWay = graph.entity(turn.to.way);
24851             var toNode = graph.entity(turn.to.node);
24852             var toVertex = graph.entity(turn.to.vertex);
24853
24854             var fromOneWay = (fromWay.tags.oneway === 'yes');
24855             var toOneWay = (toWay.tags.oneway === 'yes');
24856             var angle = (geoAngle(fromVertex, fromNode, projection) -
24857                         geoAngle(toVertex, toNode, projection)) * 180 / Math.PI;
24858
24859             while (angle < 0)
24860                 angle += 360;
24861
24862             if (fromNode === toNode)
24863                 return 'no_u_turn';
24864             if ((angle < 23 || angle > 336) && fromOneWay && toOneWay)
24865                 return 'no_u_turn';   // wider tolerance for u-turn if both ways are oneway
24866             if ((angle < 40 || angle > 319) && fromOneWay && toOneWay && turn.from.vertex !== turn.to.vertex)
24867                 return 'no_u_turn';   // even wider tolerance for u-turn if there is a via way (from !== to)
24868             if (angle < 158)
24869                 return 'no_right_turn';
24870             if (angle > 202)
24871                 return 'no_left_turn';
24872
24873             return 'no_straight_on';
24874         }
24875
24876         function actionMergePolygon(ids, newRelationId) {
24877
24878             function groupEntities(graph) {
24879                 var entities = ids.map(function (id) { return graph.entity(id); });
24880                 var geometryGroups = utilArrayGroupBy(entities, function(entity) {
24881                     if (entity.type === 'way' && entity.isClosed()) {
24882                         return 'closedWay';
24883                     } else if (entity.type === 'relation' && entity.isMultipolygon()) {
24884                         return 'multipolygon';
24885                     } else {
24886                         return 'other';
24887                     }
24888                 });
24889
24890                 return Object.assign(
24891                     { closedWay: [], multipolygon: [], other: [] },
24892                     geometryGroups
24893                 );
24894             }
24895
24896
24897             var action = function(graph) {
24898                 var entities = groupEntities(graph);
24899
24900                 // An array representing all the polygons that are part of the multipolygon.
24901                 //
24902                 // Each element is itself an array of objects with an id property, and has a
24903                 // locs property which is an array of the locations forming the polygon.
24904                 var polygons = entities.multipolygon.reduce(function(polygons, m) {
24905                     return polygons.concat(osmJoinWays(m.members, graph));
24906                 }, []).concat(entities.closedWay.map(function(d) {
24907                     var member = [{id: d.id}];
24908                     member.nodes = graph.childNodes(d);
24909                     return member;
24910                 }));
24911
24912                 // contained is an array of arrays of boolean values,
24913                 // where contained[j][k] is true iff the jth way is
24914                 // contained by the kth way.
24915                 var contained = polygons.map(function(w, i) {
24916                     return polygons.map(function(d, n) {
24917                         if (i === n) return null;
24918                         return geoPolygonContainsPolygon(
24919                             d.nodes.map(function(n) { return n.loc; }),
24920                             w.nodes.map(function(n) { return n.loc; })
24921                         );
24922                     });
24923                 });
24924
24925                 // Sort all polygons as either outer or inner ways
24926                 var members = [];
24927                 var outer = true;
24928
24929                 while (polygons.length) {
24930                     extractUncontained(polygons);
24931                     polygons = polygons.filter(isContained);
24932                     contained = contained.filter(isContained).map(filterContained);
24933                 }
24934
24935                 function isContained(d, i) {
24936                     return contained[i].some(function(val) { return val; });
24937                 }
24938
24939                 function filterContained(d) {
24940                     return d.filter(isContained);
24941                 }
24942
24943                 function extractUncontained(polygons) {
24944                     polygons.forEach(function(d, i) {
24945                         if (!isContained(d, i)) {
24946                             d.forEach(function(member) {
24947                                 members.push({
24948                                     type: 'way',
24949                                     id: member.id,
24950                                     role: outer ? 'outer' : 'inner'
24951                                 });
24952                             });
24953                         }
24954                     });
24955                     outer = !outer;
24956                 }
24957
24958                 // Move all tags to one relation
24959                 var relation = entities.multipolygon[0] ||
24960                     osmRelation({ id: newRelationId, tags: { type: 'multipolygon' }});
24961
24962                 entities.multipolygon.slice(1).forEach(function(m) {
24963                     relation = relation.mergeTags(m.tags);
24964                     graph = graph.remove(m);
24965                 });
24966
24967                 entities.closedWay.forEach(function(way) {
24968                     function isThisOuter(m) {
24969                         return m.id === way.id && m.role !== 'inner';
24970                     }
24971                     if (members.some(isThisOuter)) {
24972                         relation = relation.mergeTags(way.tags);
24973                         graph = graph.replace(way.update({ tags: {} }));
24974                     }
24975                 });
24976
24977                 return graph.replace(relation.update({
24978                     members: members,
24979                     tags: utilObjectOmit(relation.tags, ['area'])
24980                 }));
24981             };
24982
24983
24984             action.disabled = function(graph) {
24985                 var entities = groupEntities(graph);
24986                 if (entities.other.length > 0 ||
24987                     entities.closedWay.length + entities.multipolygon.length < 2) {
24988                     return 'not_eligible';
24989                 }
24990                 if (!entities.multipolygon.every(function(r) { return r.isComplete(graph); })) {
24991                     return 'incomplete_relation';
24992                 }
24993
24994                 if (!entities.multipolygon.length) {
24995                     var sharedMultipolygons = [];
24996                     entities.closedWay.forEach(function(way, i) {
24997                         if (i === 0) {
24998                             sharedMultipolygons = graph.parentMultipolygons(way);
24999                         } else {
25000                             sharedMultipolygons = utilArrayIntersection(sharedMultipolygons, graph.parentMultipolygons(way));
25001                         }
25002                     });
25003                     sharedMultipolygons = sharedMultipolygons.filter(function(relation) {
25004                         return relation.members.length === entities.closedWay.length;
25005                     });
25006                     if (sharedMultipolygons.length) {
25007                         // don't create a new multipolygon if it'd be redundant
25008                         return 'not_eligible';
25009                     }
25010                 } else if (entities.closedWay.some(function(way) {
25011                         return utilArrayIntersection(graph.parentMultipolygons(way), entities.multipolygon).length;
25012                     })) {
25013                     // don't add a way to a multipolygon again if it's already a member
25014                     return 'not_eligible';
25015                 }
25016             };
25017
25018
25019             return action;
25020         }
25021
25022         // do not edit .js files directly - edit src/index.jst
25023
25024
25025
25026         var fastDeepEqual = function equal(a, b) {
25027           if (a === b) return true;
25028
25029           if (a && b && typeof a == 'object' && typeof b == 'object') {
25030             if (a.constructor !== b.constructor) return false;
25031
25032             var length, i, keys;
25033             if (Array.isArray(a)) {
25034               length = a.length;
25035               if (length != b.length) return false;
25036               for (i = length; i-- !== 0;)
25037                 if (!equal(a[i], b[i])) return false;
25038               return true;
25039             }
25040
25041
25042
25043             if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
25044             if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
25045             if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
25046
25047             keys = Object.keys(a);
25048             length = keys.length;
25049             if (length !== Object.keys(b).length) return false;
25050
25051             for (i = length; i-- !== 0;)
25052               if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
25053
25054             for (i = length; i-- !== 0;) {
25055               var key = keys[i];
25056
25057               if (!equal(a[key], b[key])) return false;
25058             }
25059
25060             return true;
25061           }
25062
25063           // true if both NaN, false otherwise
25064           return a!==a && b!==b;
25065         };
25066
25067         // Text diff algorithm following Hunt and McIlroy 1976.
25068         // J. W. Hunt and M. D. McIlroy, An algorithm for differential buffer
25069         // comparison, Bell Telephone Laboratories CSTR #41 (1976)
25070         // http://www.cs.dartmouth.edu/~doug/
25071         // https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
25072         //
25073         // Expects two arrays, finds longest common sequence
25074         function LCS(buffer1, buffer2) {
25075
25076           let equivalenceClasses = {};
25077           for (let j = 0; j < buffer2.length; j++) {
25078             const item = buffer2[j];
25079             if (equivalenceClasses[item]) {
25080               equivalenceClasses[item].push(j);
25081             } else {
25082               equivalenceClasses[item] = [j];
25083             }
25084           }
25085
25086           const NULLRESULT = { buffer1index: -1, buffer2index: -1, chain: null };
25087           let candidates = [NULLRESULT];
25088
25089           for (let i = 0; i < buffer1.length; i++) {
25090             const item = buffer1[i];
25091             const buffer2indices = equivalenceClasses[item] || [];
25092             let r = 0;
25093             let c = candidates[0];
25094
25095             for (let jx = 0; jx < buffer2indices.length; jx++) {
25096               const j = buffer2indices[jx];
25097
25098               let s;
25099               for (s = r; s < candidates.length; s++) {
25100                 if ((candidates[s].buffer2index < j) && ((s === candidates.length - 1) || (candidates[s + 1].buffer2index > j))) {
25101                   break;
25102                 }
25103               }
25104
25105               if (s < candidates.length) {
25106                 const newCandidate = { buffer1index: i, buffer2index: j, chain: candidates[s] };
25107                 if (r === candidates.length) {
25108                   candidates.push(c);
25109                 } else {
25110                   candidates[r] = c;
25111                 }
25112                 r = s + 1;
25113                 c = newCandidate;
25114                 if (r === candidates.length) {
25115                   break; // no point in examining further (j)s
25116                 }
25117               }
25118             }
25119
25120             candidates[r] = c;
25121           }
25122
25123           // At this point, we know the LCS: it's in the reverse of the
25124           // linked-list through .chain of candidates[candidates.length - 1].
25125
25126           return candidates[candidates.length - 1];
25127         }
25128
25129
25130         // We apply the LCS to give a simple representation of the
25131         // offsets and lengths of mismatched chunks in the input
25132         // buffers. This is used by diff3MergeRegions.
25133         function diffIndices(buffer1, buffer2) {
25134           const lcs = LCS(buffer1, buffer2);
25135           let result = [];
25136           let tail1 = buffer1.length;
25137           let tail2 = buffer2.length;
25138
25139           for (let candidate = lcs; candidate !== null; candidate = candidate.chain) {
25140             const mismatchLength1 = tail1 - candidate.buffer1index - 1;
25141             const mismatchLength2 = tail2 - candidate.buffer2index - 1;
25142             tail1 = candidate.buffer1index;
25143             tail2 = candidate.buffer2index;
25144
25145             if (mismatchLength1 || mismatchLength2) {
25146               result.push({
25147                 buffer1: [tail1 + 1, mismatchLength1],
25148                 buffer1Content: buffer1.slice(tail1 + 1, tail1 + 1 + mismatchLength1),
25149                 buffer2: [tail2 + 1, mismatchLength2],
25150                 buffer2Content: buffer2.slice(tail2 + 1, tail2 + 1 + mismatchLength2)
25151               });
25152             }
25153           }
25154
25155           result.reverse();
25156           return result;
25157         }
25158
25159
25160         // Given three buffers, A, O, and B, where both A and B are
25161         // independently derived from O, returns a fairly complicated
25162         // internal representation of merge decisions it's taken. The
25163         // interested reader may wish to consult
25164         //
25165         // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce.
25166         // 'A Formal Investigation of ' In Arvind and Prasad,
25167         // editors, Foundations of Software Technology and Theoretical
25168         // Computer Science (FSTTCS), December 2007.
25169         //
25170         // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
25171         //
25172         function diff3MergeRegions(a, o, b) {
25173
25174           // "hunks" are array subsets where `a` or `b` are different from `o`
25175           // https://www.gnu.org/software/diffutils/manual/html_node/diff3-Hunks.html
25176           let hunks = [];
25177           function addHunk(h, ab) {
25178             hunks.push({
25179               ab: ab,
25180               oStart: h.buffer1[0],
25181               oLength: h.buffer1[1],   // length of o to remove
25182               abStart: h.buffer2[0],
25183               abLength: h.buffer2[1]   // length of a/b to insert
25184               // abContent: (ab === 'a' ? a : b).slice(h.buffer2[0], h.buffer2[0] + h.buffer2[1])
25185             });
25186           }
25187
25188           diffIndices(o, a).forEach(item => addHunk(item, 'a'));
25189           diffIndices(o, b).forEach(item => addHunk(item, 'b'));
25190           hunks.sort((x,y) => x.oStart - y.oStart);
25191
25192           let results = [];
25193           let currOffset = 0;
25194
25195           function advanceTo(endOffset) {
25196             if (endOffset > currOffset) {
25197               results.push({
25198                 stable: true,
25199                 buffer: 'o',
25200                 bufferStart: currOffset,
25201                 bufferLength: endOffset - currOffset,
25202                 bufferContent: o.slice(currOffset, endOffset)
25203               });
25204               currOffset = endOffset;
25205             }
25206           }
25207
25208           while (hunks.length) {
25209             let hunk = hunks.shift();
25210             let regionStart = hunk.oStart;
25211             let regionEnd = hunk.oStart + hunk.oLength;
25212             let regionHunks = [hunk];
25213             advanceTo(regionStart);
25214
25215             // Try to pull next overlapping hunk into this region
25216             while (hunks.length) {
25217               const nextHunk = hunks[0];
25218               const nextHunkStart = nextHunk.oStart;
25219               if (nextHunkStart > regionEnd) break;   // no overlap
25220
25221               regionEnd = Math.max(regionEnd, nextHunkStart + nextHunk.oLength);
25222               regionHunks.push(hunks.shift());
25223             }
25224
25225             if (regionHunks.length === 1) {
25226               // Only one hunk touches this region, meaning that there is no conflict here.
25227               // Either `a` or `b` is inserting into a region of `o` unchanged by the other.
25228               if (hunk.abLength > 0) {
25229                 const buffer = (hunk.ab === 'a' ? a : b);
25230                 results.push({
25231                   stable: true,
25232                   buffer: hunk.ab,
25233                   bufferStart: hunk.abStart,
25234                   bufferLength: hunk.abLength,
25235                   bufferContent: buffer.slice(hunk.abStart, hunk.abStart + hunk.abLength)
25236                 });
25237               }
25238             } else {
25239               // A true a/b conflict. Determine the bounds involved from `a`, `o`, and `b`.
25240               // Effectively merge all the `a` hunks into one giant hunk, then do the
25241               // same for the `b` hunks; then, correct for skew in the regions of `o`
25242               // that each side changed, and report appropriate spans for the three sides.
25243               let bounds = {
25244                 a: [a.length, -1, o.length, -1],
25245                 b: [b.length, -1, o.length, -1]
25246               };
25247               while (regionHunks.length) {
25248                 hunk = regionHunks.shift();
25249                 const oStart = hunk.oStart;
25250                 const oEnd = oStart + hunk.oLength;
25251                 const abStart = hunk.abStart;
25252                 const abEnd = abStart + hunk.abLength;
25253                 let b = bounds[hunk.ab];
25254                 b[0] = Math.min(abStart, b[0]);
25255                 b[1] = Math.max(abEnd, b[1]);
25256                 b[2] = Math.min(oStart, b[2]);
25257                 b[3] = Math.max(oEnd, b[3]);
25258               }
25259
25260               const aStart = bounds.a[0] + (regionStart - bounds.a[2]);
25261               const aEnd = bounds.a[1] + (regionEnd - bounds.a[3]);
25262               const bStart = bounds.b[0] + (regionStart - bounds.b[2]);
25263               const bEnd = bounds.b[1] + (regionEnd - bounds.b[3]);
25264
25265               let result = {
25266                 stable: false,
25267                 aStart: aStart,
25268                 aLength: aEnd - aStart,
25269                 aContent: a.slice(aStart, aEnd),
25270                 oStart: regionStart,
25271                 oLength: regionEnd - regionStart,
25272                 oContent: o.slice(regionStart, regionEnd),
25273                 bStart: bStart,
25274                 bLength: bEnd - bStart,
25275                 bContent: b.slice(bStart, bEnd)
25276               };
25277               results.push(result);
25278             }
25279             currOffset = regionEnd;
25280           }
25281
25282           advanceTo(o.length);
25283
25284           return results;
25285         }
25286
25287
25288         // Applies the output of diff3MergeRegions to actually
25289         // construct the merged buffer; the returned result alternates
25290         // between 'ok' and 'conflict' blocks.
25291         // A "false conflict" is where `a` and `b` both change the same from `o`
25292         function diff3Merge(a, o, b, options) {
25293           let defaults = {
25294             excludeFalseConflicts: true,
25295             stringSeparator: /\s+/
25296           };
25297           options = Object.assign(defaults, options);
25298
25299           const aString = (typeof a === 'string');
25300           const oString = (typeof o === 'string');
25301           const bString = (typeof b === 'string');
25302
25303           if (aString) a = a.split(options.stringSeparator);
25304           if (oString) o = o.split(options.stringSeparator);
25305           if (bString) b = b.split(options.stringSeparator);
25306
25307           let results = [];
25308           const regions = diff3MergeRegions(a, o, b);
25309
25310           let okBuffer = [];
25311           function flushOk() {
25312             if (okBuffer.length) {
25313               results.push({ ok: okBuffer });
25314             }
25315             okBuffer = [];
25316           }
25317
25318           function isFalseConflict(a, b) {
25319             if (a.length !== b.length) return false;
25320             for (let i = 0; i < a.length; i++) {
25321               if (a[i] !== b[i]) return false;
25322             }
25323             return true;
25324           }
25325
25326           regions.forEach(region =>  {
25327             if (region.stable) {
25328               okBuffer.push(...region.bufferContent);
25329             } else {
25330               if (options.excludeFalseConflicts && isFalseConflict(region.aContent, region.bContent)) {
25331                 okBuffer.push(...region.aContent);
25332               } else {
25333                 flushOk();
25334                 results.push({
25335                   conflict: {
25336                     a: region.aContent,
25337                     aIndex: region.aStart,
25338                     o: region.oContent,
25339                     oIndex: region.oStart,
25340                     b: region.bContent,
25341                     bIndex: region.bStart
25342                   }
25343                 });
25344               }
25345             }
25346           });
25347
25348           flushOk();
25349           return results;
25350         }
25351
25352         function actionMergeRemoteChanges(id, localGraph, remoteGraph, discardTags, formatUser) {
25353             discardTags = discardTags || {};
25354             var _option = 'safe';  // 'safe', 'force_local', 'force_remote'
25355             var _conflicts = [];
25356
25357
25358             function user(d) {
25359                 return (typeof formatUser === 'function') ? formatUser(d) : d;
25360             }
25361
25362
25363             function mergeLocation(remote, target) {
25364                 function pointEqual(a, b) {
25365                     var epsilon = 1e-6;
25366                     return (Math.abs(a[0] - b[0]) < epsilon) && (Math.abs(a[1] - b[1]) < epsilon);
25367                 }
25368
25369                 if (_option === 'force_local' || pointEqual(target.loc, remote.loc)) {
25370                     return target;
25371                 }
25372                 if (_option === 'force_remote') {
25373                     return target.update({loc: remote.loc});
25374                 }
25375
25376                 _conflicts.push(_t('merge_remote_changes.conflict.location', { user: user(remote.user) }));
25377                 return target;
25378             }
25379
25380
25381             function mergeNodes(base, remote, target) {
25382                 if (_option === 'force_local' || fastDeepEqual(target.nodes, remote.nodes)) {
25383                     return target;
25384                 }
25385                 if (_option === 'force_remote') {
25386                     return target.update({nodes: remote.nodes});
25387                 }
25388
25389                 var ccount = _conflicts.length;
25390                 var o = base.nodes || [];
25391                 var a = target.nodes || [];
25392                 var b = remote.nodes || [];
25393                 var nodes = [];
25394                 var hunks = diff3Merge(a, o, b, { excludeFalseConflicts: true });
25395
25396                 for (var i = 0; i < hunks.length; i++) {
25397                     var hunk = hunks[i];
25398                     if (hunk.ok) {
25399                         nodes.push.apply(nodes, hunk.ok);
25400                     } else {
25401                         // for all conflicts, we can assume c.a !== c.b
25402                         // because `diff3Merge` called with `true` option to exclude false conflicts..
25403                         var c = hunk.conflict;
25404                         if (fastDeepEqual(c.o, c.a)) {  // only changed remotely
25405                             nodes.push.apply(nodes, c.b);
25406                         } else if (fastDeepEqual(c.o, c.b)) {  // only changed locally
25407                             nodes.push.apply(nodes, c.a);
25408                         } else {       // changed both locally and remotely
25409                             _conflicts.push(_t('merge_remote_changes.conflict.nodelist', { user: user(remote.user) }));
25410                             break;
25411                         }
25412                     }
25413                 }
25414
25415                 return (_conflicts.length === ccount) ? target.update({nodes: nodes}) : target;
25416             }
25417
25418
25419             function mergeChildren(targetWay, children, updates, graph) {
25420                 function isUsed(node, targetWay) {
25421                     var hasInterestingParent = graph.parentWays(node)
25422                         .some(function(way) { return way.id !== targetWay.id; });
25423
25424                     return node.hasInterestingTags() ||
25425                         hasInterestingParent ||
25426                         graph.parentRelations(node).length > 0;
25427                 }
25428
25429                 var ccount = _conflicts.length;
25430
25431                 for (var i = 0; i < children.length; i++) {
25432                     var id = children[i];
25433                     var node = graph.hasEntity(id);
25434
25435                     // remove unused childNodes..
25436                     if (targetWay.nodes.indexOf(id) === -1) {
25437                         if (node && !isUsed(node, targetWay)) {
25438                             updates.removeIds.push(id);
25439                         }
25440                         continue;
25441                     }
25442
25443                     // restore used childNodes..
25444                     var local = localGraph.hasEntity(id);
25445                     var remote = remoteGraph.hasEntity(id);
25446                     var target;
25447
25448                     if (_option === 'force_remote' && remote && remote.visible) {
25449                         updates.replacements.push(remote);
25450
25451                     } else if (_option === 'force_local' && local) {
25452                         target = osmEntity(local);
25453                         if (remote) {
25454                             target = target.update({ version: remote.version });
25455                         }
25456                         updates.replacements.push(target);
25457
25458                     } else if (_option === 'safe' && local && remote && local.version !== remote.version) {
25459                         target = osmEntity(local, { version: remote.version });
25460                         if (remote.visible) {
25461                             target = mergeLocation(remote, target);
25462                         } else {
25463                             _conflicts.push(_t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
25464                         }
25465
25466                         if (_conflicts.length !== ccount) break;
25467                         updates.replacements.push(target);
25468                     }
25469                 }
25470
25471                 return targetWay;
25472             }
25473
25474
25475             function updateChildren(updates, graph) {
25476                 for (var i = 0; i < updates.replacements.length; i++) {
25477                     graph = graph.replace(updates.replacements[i]);
25478                 }
25479                 if (updates.removeIds.length) {
25480                     graph = actionDeleteMultiple(updates.removeIds)(graph);
25481                 }
25482                 return graph;
25483             }
25484
25485
25486             function mergeMembers(remote, target) {
25487                 if (_option === 'force_local' || fastDeepEqual(target.members, remote.members)) {
25488                     return target;
25489                 }
25490                 if (_option === 'force_remote') {
25491                     return target.update({members: remote.members});
25492                 }
25493
25494                 _conflicts.push(_t('merge_remote_changes.conflict.memberlist', { user: user(remote.user) }));
25495                 return target;
25496             }
25497
25498
25499             function mergeTags(base, remote, target) {
25500                 if (_option === 'force_local' || fastDeepEqual(target.tags, remote.tags)) {
25501                     return target;
25502                 }
25503                 if (_option === 'force_remote') {
25504                     return target.update({tags: remote.tags});
25505                 }
25506
25507                 var ccount = _conflicts.length;
25508                 var o = base.tags || {};
25509                 var a = target.tags || {};
25510                 var b = remote.tags || {};
25511                 var keys = utilArrayUnion(utilArrayUnion(Object.keys(o), Object.keys(a)), Object.keys(b))
25512                     .filter(function(k) { return !discardTags[k]; });
25513                 var tags = Object.assign({}, a);   // shallow copy
25514                 var changed = false;
25515
25516                 for (var i = 0; i < keys.length; i++) {
25517                     var k = keys[i];
25518
25519                     if (o[k] !== b[k] && a[k] !== b[k]) {    // changed remotely..
25520                         if (o[k] !== a[k]) {      // changed locally..
25521                             _conflicts.push(_t('merge_remote_changes.conflict.tags',
25522                                 { tag: k, local: a[k], remote: b[k], user: user(remote.user) }));
25523
25524                         } else {                  // unchanged locally, accept remote change..
25525                             if (b.hasOwnProperty(k)) {
25526                                 tags[k] = b[k];
25527                             } else {
25528                                 delete tags[k];
25529                             }
25530                             changed = true;
25531                         }
25532                     }
25533                 }
25534
25535                 return (changed && _conflicts.length === ccount) ? target.update({tags: tags}) : target;
25536             }
25537
25538
25539             //  `graph.base()` is the common ancestor of the two graphs.
25540             //  `localGraph` contains user's edits up to saving
25541             //  `remoteGraph` contains remote edits to modified nodes
25542             //  `graph` must be a descendent of `localGraph` and may include
25543             //      some conflict resolution actions performed on it.
25544             //
25545             //                  --- ... --- `localGraph` -- ... -- `graph`
25546             //                 /
25547             //  `graph.base()` --- ... --- `remoteGraph`
25548             //
25549             var action = function(graph) {
25550                 var updates = { replacements: [], removeIds: [] };
25551                 var base = graph.base().entities[id];
25552                 var local = localGraph.entity(id);
25553                 var remote = remoteGraph.entity(id);
25554                 var target = osmEntity(local, { version: remote.version });
25555
25556                 // delete/undelete
25557                 if (!remote.visible) {
25558                     if (_option === 'force_remote') {
25559                         return actionDeleteMultiple([id])(graph);
25560
25561                     } else if (_option === 'force_local') {
25562                         if (target.type === 'way') {
25563                             target = mergeChildren(target, utilArrayUniq(local.nodes), updates, graph);
25564                             graph = updateChildren(updates, graph);
25565                         }
25566                         return graph.replace(target);
25567
25568                     } else {
25569                         _conflicts.push(_t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
25570                         return graph;  // do nothing
25571                     }
25572                 }
25573
25574                 // merge
25575                 if (target.type === 'node') {
25576                     target = mergeLocation(remote, target);
25577
25578                 } else if (target.type === 'way') {
25579                     // pull in any child nodes that may not be present locally..
25580                     graph.rebase(remoteGraph.childNodes(remote), [graph], false);
25581                     target = mergeNodes(base, remote, target);
25582                     target = mergeChildren(target, utilArrayUnion(local.nodes, remote.nodes), updates, graph);
25583
25584                 } else if (target.type === 'relation') {
25585                     target = mergeMembers(remote, target);
25586                 }
25587
25588                 target = mergeTags(base, remote, target);
25589
25590                 if (!_conflicts.length) {
25591                     graph = updateChildren(updates, graph).replace(target);
25592                 }
25593
25594                 return graph;
25595             };
25596
25597
25598             action.withOption = function(opt) {
25599                 _option = opt;
25600                 return action;
25601             };
25602
25603
25604             action.conflicts = function() {
25605                 return _conflicts;
25606             };
25607
25608
25609             return action;
25610         }
25611
25612         // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
25613         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
25614         function actionMove(moveIDs, tryDelta, projection, cache) {
25615             var _delta = tryDelta;
25616
25617             function setupCache(graph) {
25618                 function canMove(nodeID) {
25619                     // Allow movement of any node that is in the selectedIDs list..
25620                     if (moveIDs.indexOf(nodeID) !== -1) return true;
25621
25622                     // Allow movement of a vertex where 2 ways meet..
25623                     var parents = graph.parentWays(graph.entity(nodeID));
25624                     if (parents.length < 3) return true;
25625
25626                     // Restrict movement of a vertex where >2 ways meet, unless all parentWays are moving too..
25627                     var parentsMoving = parents.every(function(way) { return cache.moving[way.id]; });
25628                     if (!parentsMoving) delete cache.moving[nodeID];
25629
25630                     return parentsMoving;
25631                 }
25632
25633                 function cacheEntities(ids) {
25634                     for (var i = 0; i < ids.length; i++) {
25635                         var id = ids[i];
25636                         if (cache.moving[id]) continue;
25637                         cache.moving[id] = true;
25638
25639                         var entity = graph.hasEntity(id);
25640                         if (!entity) continue;
25641
25642                         if (entity.type === 'node') {
25643                             cache.nodes.push(id);
25644                             cache.startLoc[id] = entity.loc;
25645                         } else if (entity.type === 'way') {
25646                             cache.ways.push(id);
25647                             cacheEntities(entity.nodes);
25648                         } else {
25649                             cacheEntities(entity.members.map(function(member) {
25650                                 return member.id;
25651                             }));
25652                         }
25653                     }
25654                 }
25655
25656                 function cacheIntersections(ids) {
25657                     function isEndpoint(way, id) {
25658                         return !way.isClosed() && !!way.affix(id);
25659                     }
25660
25661                     for (var i = 0; i < ids.length; i++) {
25662                         var id = ids[i];
25663
25664                         // consider only intersections with 1 moved and 1 unmoved way.
25665                         var childNodes = graph.childNodes(graph.entity(id));
25666                         for (var j = 0; j < childNodes.length; j++) {
25667                             var node = childNodes[j];
25668                             var parents = graph.parentWays(node);
25669                             if (parents.length !== 2) continue;
25670
25671                             var moved = graph.entity(id);
25672                             var unmoved = null;
25673                             for (var k = 0; k < parents.length; k++) {
25674                                 var way = parents[k];
25675                                 if (!cache.moving[way.id]) {
25676                                     unmoved = way;
25677                                     break;
25678                                 }
25679                             }
25680                             if (!unmoved) continue;
25681
25682                             // exclude ways that are overly connected..
25683                             if (utilArrayIntersection(moved.nodes, unmoved.nodes).length > 2) continue;
25684                             if (moved.isArea() || unmoved.isArea()) continue;
25685
25686                             cache.intersections.push({
25687                                 nodeId: node.id,
25688                                 movedId: moved.id,
25689                                 unmovedId: unmoved.id,
25690                                 movedIsEP: isEndpoint(moved, node.id),
25691                                 unmovedIsEP: isEndpoint(unmoved, node.id)
25692                             });
25693                         }
25694                     }
25695                 }
25696
25697
25698                 if (!cache) {
25699                     cache = {};
25700                 }
25701                 if (!cache.ok) {
25702                     cache.moving = {};
25703                     cache.intersections = [];
25704                     cache.replacedVertex = {};
25705                     cache.startLoc = {};
25706                     cache.nodes = [];
25707                     cache.ways = [];
25708
25709                     cacheEntities(moveIDs);
25710                     cacheIntersections(cache.ways);
25711                     cache.nodes = cache.nodes.filter(canMove);
25712
25713                     cache.ok = true;
25714                 }
25715             }
25716
25717
25718             // Place a vertex where the moved vertex used to be, to preserve way shape..
25719             //
25720             //  Start:
25721             //      b ---- e
25722             //     / \
25723             //    /   \
25724             //   /     \
25725             //  a       c
25726             //
25727             //      *               node '*' added to preserve shape
25728             //     / \
25729             //    /   b ---- e      way `b,e` moved here:
25730             //   /     \
25731             //  a       c
25732             //
25733             //
25734             function replaceMovedVertex(nodeId, wayId, graph, delta) {
25735                 var way = graph.entity(wayId);
25736                 var moved = graph.entity(nodeId);
25737                 var movedIndex = way.nodes.indexOf(nodeId);
25738                 var len, prevIndex, nextIndex;
25739
25740                 if (way.isClosed()) {
25741                     len = way.nodes.length - 1;
25742                     prevIndex = (movedIndex + len - 1) % len;
25743                     nextIndex = (movedIndex + len + 1) % len;
25744                 } else {
25745                     len = way.nodes.length;
25746                     prevIndex = movedIndex - 1;
25747                     nextIndex = movedIndex + 1;
25748                 }
25749
25750                 var prev = graph.hasEntity(way.nodes[prevIndex]);
25751                 var next = graph.hasEntity(way.nodes[nextIndex]);
25752
25753                 // Don't add orig vertex at endpoint..
25754                 if (!prev || !next) return graph;
25755
25756                 var key = wayId + '_' + nodeId;
25757                 var orig = cache.replacedVertex[key];
25758                 if (!orig) {
25759                     orig = osmNode();
25760                     cache.replacedVertex[key] = orig;
25761                     cache.startLoc[orig.id] = cache.startLoc[nodeId];
25762                 }
25763
25764                 var start, end;
25765                 if (delta) {
25766                     start = projection(cache.startLoc[nodeId]);
25767                     end = projection.invert(geoVecAdd(start, delta));
25768                 } else {
25769                     end = cache.startLoc[nodeId];
25770                 }
25771                 orig = orig.move(end);
25772
25773                 var angle = Math.abs(geoAngle(orig, prev, projection) -
25774                         geoAngle(orig, next, projection)) * 180 / Math.PI;
25775
25776                 // Don't add orig vertex if it would just make a straight line..
25777                 if (angle > 175 && angle < 185) return graph;
25778
25779                 // moving forward or backward along way?
25780                 var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection);
25781                 var p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection);
25782                 var d1 = geoPathLength(p1);
25783                 var d2 = geoPathLength(p2);
25784                 var insertAt = (d1 <= d2) ? movedIndex : nextIndex;
25785
25786                 // moving around closed loop?
25787                 if (way.isClosed() && insertAt === 0) insertAt = len;
25788
25789                 way = way.addNode(orig.id, insertAt);
25790                 return graph.replace(orig).replace(way);
25791             }
25792
25793
25794             // Remove duplicate vertex that might have been added by
25795             // replaceMovedVertex.  This is done after the unzorro checks.
25796             function removeDuplicateVertices(wayId, graph) {
25797                 var way = graph.entity(wayId);
25798                 var epsilon = 1e-6;
25799                 var prev, curr;
25800
25801                 function isInteresting(node, graph) {
25802                     return graph.parentWays(node).length > 1 ||
25803                         graph.parentRelations(node).length ||
25804                         node.hasInterestingTags();
25805                 }
25806
25807                 for (var i = 0; i < way.nodes.length; i++) {
25808                     curr = graph.entity(way.nodes[i]);
25809
25810                     if (prev && curr && geoVecEqual(prev.loc, curr.loc, epsilon)) {
25811                         if (!isInteresting(prev, graph)) {
25812                             way = way.removeNode(prev.id);
25813                             graph = graph.replace(way).remove(prev);
25814                         } else if (!isInteresting(curr, graph)) {
25815                             way = way.removeNode(curr.id);
25816                             graph = graph.replace(way).remove(curr);
25817                         }
25818                     }
25819
25820                     prev = curr;
25821                 }
25822
25823                 return graph;
25824             }
25825
25826
25827             // Reorder nodes around intersections that have moved..
25828             //
25829             //  Start:                way1.nodes: b,e         (moving)
25830             //  a - b - c ----- d     way2.nodes: a,b,c,d     (static)
25831             //      |                 vertex: b
25832             //      e                 isEP1: true,  isEP2, false
25833             //
25834             //  way1 `b,e` moved here:
25835             //  a ----- c = b - d
25836             //              |
25837             //              e
25838             //
25839             //  reorder nodes         way1.nodes: b,e
25840             //  a ----- c - b - d     way2.nodes: a,c,b,d
25841             //              |
25842             //              e
25843             //
25844             function unZorroIntersection(intersection, graph) {
25845                 var vertex = graph.entity(intersection.nodeId);
25846                 var way1 = graph.entity(intersection.movedId);
25847                 var way2 = graph.entity(intersection.unmovedId);
25848                 var isEP1 = intersection.movedIsEP;
25849                 var isEP2 = intersection.unmovedIsEP;
25850
25851                 // don't move the vertex if it is the endpoint of both ways.
25852                 if (isEP1 && isEP2) return graph;
25853
25854                 var nodes1 = graph.childNodes(way1).filter(function(n) { return n !== vertex; });
25855                 var nodes2 = graph.childNodes(way2).filter(function(n) { return n !== vertex; });
25856
25857                 if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
25858                 if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
25859
25860                 var edge1 = !isEP1 && geoChooseEdge(nodes1, projection(vertex.loc), projection);
25861                 var edge2 = !isEP2 && geoChooseEdge(nodes2, projection(vertex.loc), projection);
25862                 var loc;
25863
25864                 // snap vertex to nearest edge (or some point between them)..
25865                 if (!isEP1 && !isEP2) {
25866                     var epsilon = 1e-6, maxIter = 10;
25867                     for (var i = 0; i < maxIter; i++) {
25868                         loc = geoVecInterp(edge1.loc, edge2.loc, 0.5);
25869                         edge1 = geoChooseEdge(nodes1, projection(loc), projection);
25870                         edge2 = geoChooseEdge(nodes2, projection(loc), projection);
25871                         if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
25872                     }
25873                 } else if (!isEP1) {
25874                     loc = edge1.loc;
25875                 } else {
25876                     loc = edge2.loc;
25877                 }
25878
25879                 graph = graph.replace(vertex.move(loc));
25880
25881                 // if zorro happened, reorder nodes..
25882                 if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
25883                     way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
25884                     graph = graph.replace(way1);
25885                 }
25886                 if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
25887                     way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
25888                     graph = graph.replace(way2);
25889                 }
25890
25891                 return graph;
25892             }
25893
25894
25895             function cleanupIntersections(graph) {
25896                 for (var i = 0; i < cache.intersections.length; i++) {
25897                     var obj = cache.intersections[i];
25898                     graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, _delta);
25899                     graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
25900                     graph = unZorroIntersection(obj, graph);
25901                     graph = removeDuplicateVertices(obj.movedId, graph);
25902                     graph = removeDuplicateVertices(obj.unmovedId, graph);
25903                 }
25904
25905                 return graph;
25906             }
25907
25908
25909             // check if moving way endpoint can cross an unmoved way, if so limit delta..
25910             function limitDelta(graph) {
25911                 function moveNode(loc) {
25912                     return geoVecAdd(projection(loc), _delta);
25913                 }
25914
25915                 for (var i = 0; i < cache.intersections.length; i++) {
25916                     var obj = cache.intersections[i];
25917
25918                     // Don't limit movement if this is vertex joins 2 endpoints..
25919                     if (obj.movedIsEP && obj.unmovedIsEP) continue;
25920                     // Don't limit movement if this vertex is not an endpoint anyway..
25921                     if (!obj.movedIsEP) continue;
25922
25923                     var node = graph.entity(obj.nodeId);
25924                     var start = projection(node.loc);
25925                     var end = geoVecAdd(start, _delta);
25926                     var movedNodes = graph.childNodes(graph.entity(obj.movedId));
25927                     var movedPath = movedNodes.map(function(n) { return moveNode(n.loc); });
25928                     var unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId));
25929                     var unmovedPath = unmovedNodes.map(function(n) { return projection(n.loc); });
25930                     var hits = geoPathIntersections(movedPath, unmovedPath);
25931
25932                     for (var j = 0; i < hits.length; i++) {
25933                         if (geoVecEqual(hits[j], end)) continue;
25934                         var edge = geoChooseEdge(unmovedNodes, end, projection);
25935                         _delta = geoVecSubtract(projection(edge.loc), start);
25936                     }
25937                 }
25938             }
25939
25940
25941             var action = function(graph) {
25942                 if (_delta[0] === 0 && _delta[1] === 0) return graph;
25943
25944                 setupCache(graph);
25945
25946                 if (cache.intersections.length) {
25947                     limitDelta(graph);
25948                 }
25949
25950                 for (var i = 0; i < cache.nodes.length; i++) {
25951                     var node = graph.entity(cache.nodes[i]);
25952                     var start = projection(node.loc);
25953                     var end = geoVecAdd(start, _delta);
25954                     graph = graph.replace(node.move(projection.invert(end)));
25955                 }
25956
25957                 if (cache.intersections.length) {
25958                     graph = cleanupIntersections(graph);
25959                 }
25960
25961                 return graph;
25962             };
25963
25964
25965             action.delta = function() {
25966                 return _delta;
25967             };
25968
25969
25970             return action;
25971         }
25972
25973         function actionMoveMember(relationId, fromIndex, toIndex) {
25974             return function(graph) {
25975                 return graph.replace(graph.entity(relationId).moveMember(fromIndex, toIndex));
25976             };
25977         }
25978
25979         function actionMoveNode(nodeID, toLoc) {
25980
25981             var action = function(graph, t) {
25982                 if (t === null || !isFinite(t)) t = 1;
25983                 t = Math.min(Math.max(+t, 0), 1);
25984
25985                 var node = graph.entity(nodeID);
25986                 return graph.replace(
25987                     node.move(geoVecInterp(node.loc, toLoc, t))
25988                 );
25989             };
25990
25991             action.transitionable = true;
25992
25993             return action;
25994         }
25995
25996         function actionNoop() {
25997             return function(graph) {
25998                 return graph;
25999             };
26000         }
26001
26002         function actionOrthogonalize(wayID, projection, vertexID, degThresh, ep) {
26003             var epsilon = ep || 1e-4;
26004             var threshold = degThresh || 13;  // degrees within right or straight to alter
26005
26006             // We test normalized dot products so we can compare as cos(angle)
26007             var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
26008             var upperThreshold = Math.cos(threshold * Math.PI / 180);
26009
26010
26011             var action = function(graph, t) {
26012                 if (t === null || !isFinite(t)) t = 1;
26013                 t = Math.min(Math.max(+t, 0), 1);
26014
26015                 var way = graph.entity(wayID);
26016                 way = way.removeNode('');   // sanity check - remove any consecutive duplicates
26017
26018                 if (way.tags.nonsquare) {
26019                     var tags = Object.assign({}, way.tags);
26020                     // since we're squaring, remove indication that this is physically unsquare
26021                     delete tags.nonsquare;
26022                     way = way.update({tags: tags});
26023                 }
26024
26025                 graph = graph.replace(way);
26026
26027                 var isClosed = way.isClosed();
26028                 var nodes = graph.childNodes(way).slice();  // shallow copy
26029                 if (isClosed) nodes.pop();
26030
26031                 if (vertexID !== undefined) {
26032                     nodes = nodeSubset(nodes, vertexID, isClosed);
26033                     if (nodes.length !== 3) return graph;
26034                 }
26035
26036                 // note: all geometry functions here use the unclosed node/point/coord list
26037
26038                 var nodeCount = {};
26039                 var points = [];
26040                 var corner = { i: 0, dotp: 1 };
26041                 var node, point, loc, score, motions, i, j;
26042
26043                 for (i = 0; i < nodes.length; i++) {
26044                     node = nodes[i];
26045                     nodeCount[node.id] = (nodeCount[node.id] || 0) + 1;
26046                     points.push({ id: node.id, coord: projection(node.loc) });
26047                 }
26048
26049
26050                 if (points.length === 3) {   // move only one vertex for right triangle
26051                     for (i = 0; i < 1000; i++) {
26052                         motions = points.map(calcMotion);
26053
26054                         points[corner.i].coord = geoVecAdd(points[corner.i].coord, motions[corner.i]);
26055                         score = corner.dotp;
26056                         if (score < epsilon) {
26057                             break;
26058                         }
26059                     }
26060
26061                     node = graph.entity(nodes[corner.i].id);
26062                     loc = projection.invert(points[corner.i].coord);
26063                     graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
26064
26065                 } else {
26066                     var straights = [];
26067                     var simplified = [];
26068
26069                     // Remove points from nearly straight sections..
26070                     // This produces a simplified shape to orthogonalize
26071                     for (i = 0; i < points.length; i++) {
26072                         point = points[i];
26073                         var dotp = 0;
26074                         if (isClosed || (i > 0 && i < points.length - 1)) {
26075                             var a = points[(i - 1 + points.length) % points.length];
26076                             var b = points[(i + 1) % points.length];
26077                             dotp = Math.abs(geoOrthoNormalizedDotProduct(a.coord, b.coord, point.coord));
26078                         }
26079
26080                         if (dotp > upperThreshold) {
26081                             straights.push(point);
26082                         } else {
26083                             simplified.push(point);
26084                         }
26085                     }
26086
26087                     // Orthogonalize the simplified shape
26088                     var bestPoints = clonePoints(simplified);
26089                     var originalPoints = clonePoints(simplified);
26090
26091                     score = Infinity;
26092                     for (i = 0; i < 1000; i++) {
26093                         motions = simplified.map(calcMotion);
26094
26095                         for (j = 0; j < motions.length; j++) {
26096                             simplified[j].coord = geoVecAdd(simplified[j].coord, motions[j]);
26097                         }
26098                         var newScore = geoOrthoCalcScore(simplified, isClosed, epsilon, threshold);
26099                         if (newScore < score) {
26100                             bestPoints = clonePoints(simplified);
26101                             score = newScore;
26102                         }
26103                         if (score < epsilon) {
26104                             break;
26105                         }
26106                     }
26107
26108                     var bestCoords = bestPoints.map(function(p) { return p.coord; });
26109                     if (isClosed) bestCoords.push(bestCoords[0]);
26110
26111                     // move the nodes that should move
26112                     for (i = 0; i < bestPoints.length; i++) {
26113                         point = bestPoints[i];
26114                         if (!geoVecEqual(originalPoints[i].coord, point.coord)) {
26115                             node = graph.entity(point.id);
26116                             loc = projection.invert(point.coord);
26117                             graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
26118                         }
26119                     }
26120
26121                     // move the nodes along straight segments
26122                     for (i = 0; i < straights.length; i++) {
26123                         point = straights[i];
26124                         if (nodeCount[point.id] > 1) continue;   // skip self-intersections
26125
26126                         node = graph.entity(point.id);
26127
26128                         if (t === 1 &&
26129                             graph.parentWays(node).length === 1 &&
26130                             graph.parentRelations(node).length === 0 &&
26131                             !node.hasInterestingTags()
26132                         ) {
26133                             // remove uninteresting points..
26134                             graph = actionDeleteNode(node.id)(graph);
26135
26136                         } else {
26137                             // move interesting points to the nearest edge..
26138                             var choice = geoVecProject(point.coord, bestCoords);
26139                             if (choice) {
26140                                 loc = projection.invert(choice.target);
26141                                 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
26142                             }
26143                         }
26144                     }
26145                 }
26146
26147                 return graph;
26148
26149
26150                 function clonePoints(array) {
26151                     return array.map(function(p) {
26152                         return { id: p.id, coord: [p.coord[0], p.coord[1]] };
26153                     });
26154                 }
26155
26156
26157                 function calcMotion(point, i, array) {
26158                     // don't try to move the endpoints of a non-closed way.
26159                     if (!isClosed && (i === 0 || i === array.length - 1)) return [0, 0];
26160                     // don't try to move a node that appears more than once (self intersection)
26161                     if (nodeCount[array[i].id] > 1) return [0, 0];
26162
26163                     var a = array[(i - 1 + array.length) % array.length].coord;
26164                     var origin = point.coord;
26165                     var b = array[(i + 1) % array.length].coord;
26166                     var p = geoVecSubtract(a, origin);
26167                     var q = geoVecSubtract(b, origin);
26168
26169                     var scale = 2 * Math.min(geoVecLength(p), geoVecLength(q));
26170                     p = geoVecNormalize(p);
26171                     q = geoVecNormalize(q);
26172
26173                     var dotp = (p[0] * q[0] + p[1] * q[1]);
26174                     var val = Math.abs(dotp);
26175
26176                     if (val < lowerThreshold) {  // nearly orthogonal
26177                         corner.i = i;
26178                         corner.dotp = val;
26179                         var vec = geoVecNormalize(geoVecAdd(p, q));
26180                         return geoVecScale(vec, 0.1 * dotp * scale);
26181                     }
26182
26183                     return [0, 0];   // do nothing
26184                 }
26185             };
26186
26187
26188             // if we are only orthogonalizing one vertex,
26189             // get that vertex and the previous and next
26190             function nodeSubset(nodes, vertexID, isClosed) {
26191                 var first = isClosed ? 0 : 1;
26192                 var last = isClosed ? nodes.length : nodes.length - 1;
26193
26194                 for (var i = first; i < last; i++) {
26195                     if (nodes[i].id === vertexID) {
26196                         return [
26197                             nodes[(i - 1 + nodes.length) % nodes.length],
26198                             nodes[i],
26199                             nodes[(i + 1) % nodes.length]
26200                         ];
26201                     }
26202                 }
26203
26204                 return [];
26205             }
26206
26207
26208             action.disabled = function(graph) {
26209                 var way = graph.entity(wayID);
26210                 way = way.removeNode('');  // sanity check - remove any consecutive duplicates
26211                 graph = graph.replace(way);
26212
26213                 var isClosed = way.isClosed();
26214                 var nodes = graph.childNodes(way).slice();  // shallow copy
26215                 if (isClosed) nodes.pop();
26216
26217                 var allowStraightAngles = false;
26218                 if (vertexID !== undefined) {
26219                     allowStraightAngles = true;
26220                     nodes = nodeSubset(nodes, vertexID, isClosed);
26221                     if (nodes.length !== 3) return 'end_vertex';
26222                 }
26223
26224                 var coords = nodes.map(function(n) { return projection(n.loc); });
26225                 var score = geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles);
26226
26227                 if (score === null) {
26228                     return 'not_squarish';
26229                 } else if (score === 0) {
26230                     return 'square_enough';
26231                 } else {
26232                     return false;
26233                 }
26234             };
26235
26236
26237             action.transitionable = true;
26238
26239             return action;
26240         }
26241
26242         // `actionRestrictTurn` creates a turn restriction relation.
26243         //
26244         // `turn` must be an `osmTurn` object
26245         // see osm/intersection.js, pathToTurn()
26246         //
26247         // This specifies a restriction of type `restriction` when traveling from
26248         // `turn.from.way` toward `turn.to.way` via `turn.via.node` OR `turn.via.ways`.
26249         // (The action does not check that these entities form a valid intersection.)
26250         //
26251         // From, to, and via ways should be split before calling this action.
26252         // (old versions of the code would split the ways here, but we no longer do it)
26253         //
26254         // For testing convenience, accepts a restrictionID to assign to the new
26255         // relation. Normally, this will be undefined and the relation will
26256         // automatically be assigned a new ID.
26257         //
26258         function actionRestrictTurn(turn, restrictionType, restrictionID) {
26259
26260             return function(graph) {
26261                 var fromWay = graph.entity(turn.from.way);
26262                 var toWay = graph.entity(turn.to.way);
26263                 var viaNode = turn.via.node && graph.entity(turn.via.node);
26264                 var viaWays = turn.via.ways && turn.via.ways.map(function(id) { return graph.entity(id); });
26265                 var members = [];
26266
26267                 members.push({ id: fromWay.id, type: 'way',  role: 'from' });
26268
26269                 if (viaNode) {
26270                     members.push({ id: viaNode.id,  type: 'node', role: 'via' });
26271                 } else if (viaWays) {
26272                     viaWays.forEach(function(viaWay) {
26273                         members.push({ id: viaWay.id,  type: 'way', role: 'via' });
26274                     });
26275                 }
26276
26277                 members.push({ id: toWay.id, type: 'way',  role: 'to' });
26278
26279                 return graph.replace(osmRelation({
26280                     id: restrictionID,
26281                     tags: {
26282                         type: 'restriction',
26283                         restriction: restrictionType
26284                     },
26285                     members: members
26286                 }));
26287             };
26288         }
26289
26290         function actionRevert(id) {
26291             var action = function(graph) {
26292                 var entity = graph.hasEntity(id),
26293                     base = graph.base().entities[id];
26294
26295                 if (entity && !base) {    // entity will be removed..
26296                     if (entity.type === 'node') {
26297                         graph.parentWays(entity)
26298                             .forEach(function(parent) {
26299                                 parent = parent.removeNode(id);
26300                                 graph = graph.replace(parent);
26301
26302                                 if (parent.isDegenerate()) {
26303                                     graph = actionDeleteWay(parent.id)(graph);
26304                                 }
26305                             });
26306                     }
26307
26308                     graph.parentRelations(entity)
26309                         .forEach(function(parent) {
26310                             parent = parent.removeMembersWithID(id);
26311                             graph = graph.replace(parent);
26312
26313                             if (parent.isDegenerate()) {
26314                                 graph = actionDeleteRelation(parent.id)(graph);
26315                             }
26316                         });
26317                 }
26318
26319                 return graph.revert(id);
26320             };
26321
26322             return action;
26323         }
26324
26325         function actionRotate(rotateIds, pivot, angle, projection) {
26326
26327             var action = function(graph) {
26328                 return graph.update(function(graph) {
26329                     utilGetAllNodes(rotateIds, graph).forEach(function(node) {
26330                         var point = geoRotate([projection(node.loc)], angle, pivot)[0];
26331                         graph = graph.replace(node.move(projection.invert(point)));
26332                     });
26333                 });
26334             };
26335
26336             return action;
26337         }
26338
26339         /* Align nodes along their common axis */
26340         function actionStraightenNodes(nodeIDs, projection) {
26341
26342             function positionAlongWay(a, o, b) {
26343                 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
26344             }
26345
26346             // returns the endpoints of the long axis of symmetry of the `points` bounding rect 
26347             function getEndpoints(points) {
26348                 var ssr = geoGetSmallestSurroundingRectangle(points);
26349
26350                 // Choose line pq = axis of symmetry.
26351                 // The shape's surrounding rectangle has 2 axes of symmetry.
26352                 // Snap points to the long axis
26353                 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2 ];
26354                 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2 ];
26355                 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2 ];
26356                 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2 ];
26357
26358                 var isLong = (geoVecLength(p1, q1) > geoVecLength(p2, q2));
26359                 if (isLong) {
26360                     return [p1, q1];
26361                 }
26362                 return [p2, q2];
26363             }
26364
26365
26366             var action = function(graph, t) {
26367                 if (t === null || !isFinite(t)) t = 1;
26368                 t = Math.min(Math.max(+t, 0), 1);
26369
26370                 var nodes = nodeIDs.map(function(id) { return graph.entity(id); });
26371                 var points = nodes.map(function(n) { return projection(n.loc); });
26372                 var endpoints = getEndpoints(points);
26373                 var startPoint = endpoints[0];
26374                 var endPoint = endpoints[1];
26375
26376                 // Move points onto the line connecting the endpoints
26377                 for (var i = 0; i < points.length; i++) {
26378                     var node = nodes[i];
26379                     var point = points[i];
26380                     var u = positionAlongWay(point, startPoint, endPoint);
26381                     var point2 = geoVecInterp(startPoint, endPoint, u);
26382                     var loc2 = projection.invert(point2);
26383                     graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
26384                 }
26385
26386                 return graph;
26387             };
26388
26389
26390             action.disabled = function(graph) {
26391
26392                 var nodes = nodeIDs.map(function(id) { return graph.entity(id); });
26393                 var points = nodes.map(function(n) { return projection(n.loc); });
26394                 var endpoints = getEndpoints(points);
26395                 var startPoint = endpoints[0];
26396                 var endPoint = endpoints[1];
26397
26398                 var maxDistance = 0;
26399
26400                 for (var i = 0; i < points.length; i++) {
26401                     var point = points[i];
26402                     var u = positionAlongWay(point, startPoint, endPoint);
26403                     var p = geoVecInterp(startPoint, endPoint, u);
26404                     var dist = geoVecLength(p, point);
26405
26406                     if (!isNaN(dist) && dist > maxDistance) {
26407                         maxDistance = dist;
26408                     }
26409                 }
26410
26411                 if (maxDistance < 0.0001) {
26412                     return 'straight_enough';
26413                 }
26414             };
26415
26416
26417             action.transitionable = true;
26418
26419
26420             return action;
26421         }
26422
26423         /*
26424          * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
26425          */
26426         function actionStraightenWay(selectedIDs, projection) {
26427
26428             function positionAlongWay(a, o, b) {
26429                 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
26430             }
26431
26432             // Return all selected ways as a continuous, ordered array of nodes
26433             function allNodes(graph) {
26434                 var nodes = [];
26435                 var startNodes = [];
26436                 var endNodes = [];
26437                 var remainingWays = [];
26438                 var selectedWays = selectedIDs.filter(function(w) {
26439                     return graph.entity(w).type === 'way';
26440                 });
26441                 var selectedNodes = selectedIDs.filter(function(n) {
26442                     return graph.entity(n).type === 'node';
26443                 });
26444
26445                 for (var i = 0; i < selectedWays.length; i++) {
26446                     var way = graph.entity(selectedWays[i]);
26447                     nodes = way.nodes.slice(0);
26448                     remainingWays.push(nodes);
26449                     startNodes.push(nodes[0]);
26450                     endNodes.push(nodes[nodes.length-1]);
26451                 }
26452
26453                 // Remove duplicate end/startNodes (duplicate nodes cannot be at the line end,
26454                 //   and need to be removed so currNode difference calculation below works)
26455                 // i.e. ["n-1", "n-1", "n-2"] => ["n-2"]
26456                 startNodes = startNodes.filter(function(n) {
26457                     return startNodes.indexOf(n) === startNodes.lastIndexOf(n);
26458                 });
26459                 endNodes = endNodes.filter(function(n) {
26460                     return endNodes.indexOf(n) === endNodes.lastIndexOf(n);
26461                 });
26462
26463                 // Choose the initial endpoint to start from
26464                 var currNode = utilArrayDifference(startNodes, endNodes)
26465                     .concat(utilArrayDifference(endNodes, startNodes))[0];
26466                 var nextWay = [];
26467                 nodes = [];
26468
26469                 // Create nested function outside of loop to avoid "function in loop" lint error
26470                 var getNextWay = function(currNode, remainingWays) {
26471                     return remainingWays.filter(function(way) {
26472                         return way[0] === currNode || way[way.length-1] === currNode;
26473                     })[0];
26474                 };
26475
26476                 // Add nodes to end of nodes array, until all ways are added
26477                 while (remainingWays.length) {
26478                     nextWay = getNextWay(currNode, remainingWays);
26479                     remainingWays = utilArrayDifference(remainingWays, [nextWay]);
26480
26481                     if (nextWay[0] !== currNode) {
26482                         nextWay.reverse();
26483                     }
26484                     nodes = nodes.concat(nextWay);
26485                     currNode = nodes[nodes.length-1];
26486                 }
26487
26488                 // If user selected 2 nodes to straighten between, then slice nodes array to those nodes
26489                 if (selectedNodes.length === 2) {
26490                     var startNodeIdx = nodes.indexOf(selectedNodes[0]);
26491                     var endNodeIdx = nodes.indexOf(selectedNodes[1]);
26492                     var sortedStartEnd = [startNodeIdx, endNodeIdx];
26493
26494                     sortedStartEnd.sort(function(a, b) { return a - b; });
26495                     nodes = nodes.slice(sortedStartEnd[0], sortedStartEnd[1]+1);
26496                 }
26497
26498                 return nodes.map(function(n) { return graph.entity(n); });
26499             }
26500
26501             function shouldKeepNode(node, graph) {
26502                 return graph.parentWays(node).length > 1 ||
26503                     graph.parentRelations(node).length ||
26504                     node.hasInterestingTags();
26505             }
26506
26507
26508             var action = function(graph, t) {
26509                 if (t === null || !isFinite(t)) t = 1;
26510                 t = Math.min(Math.max(+t, 0), 1);
26511
26512                 var nodes = allNodes(graph);
26513                 var points = nodes.map(function(n) { return projection(n.loc); });
26514                 var startPoint = points[0];
26515                 var endPoint = points[points.length-1];
26516                 var toDelete = [];
26517                 var i;
26518
26519                 for (i = 1; i < points.length-1; i++) {
26520                     var node = nodes[i];
26521                     var point = points[i];
26522
26523                     if (t < 1 || shouldKeepNode(node, graph)) {
26524                         var u = positionAlongWay(point, startPoint, endPoint);
26525                         var p = geoVecInterp(startPoint, endPoint, u);
26526                         var loc2 = projection.invert(p);
26527                         graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
26528
26529                     } else {
26530                         // safe to delete
26531                         if (toDelete.indexOf(node) === -1) {
26532                             toDelete.push(node);
26533                         }
26534                     }
26535                 }
26536
26537                 for (i = 0; i < toDelete.length; i++) {
26538                     graph = actionDeleteNode(toDelete[i].id)(graph);
26539                 }
26540
26541                 return graph;
26542             };
26543
26544
26545             action.disabled = function(graph) {
26546                 // check way isn't too bendy
26547                 var nodes = allNodes(graph);
26548                 var points = nodes.map(function(n) { return projection(n.loc); });
26549                 var startPoint = points[0];
26550                 var endPoint = points[points.length-1];
26551                 var threshold = 0.2 * geoVecLength(startPoint, endPoint);
26552                 var i;
26553
26554                 if (threshold === 0) {
26555                     return 'too_bendy';
26556                 }
26557
26558                 var maxDistance = 0;
26559
26560                 for (i = 1; i < points.length - 1; i++) {
26561                     var point = points[i];
26562                     var u = positionAlongWay(point, startPoint, endPoint);
26563                     var p = geoVecInterp(startPoint, endPoint, u);
26564                     var dist = geoVecLength(p, point);
26565
26566                     // to bendy if point is off by 20% of total start/end distance in projected space
26567                     if (isNaN(dist) || dist > threshold) {
26568                         return 'too_bendy';
26569                     } else if (dist > maxDistance) {
26570                         maxDistance = dist;
26571                     }
26572                 }
26573
26574                 var keepingAllNodes = nodes.every(function(node, i) {
26575                     return i === 0 || i === nodes.length - 1 || shouldKeepNode(node, graph);
26576                 });
26577
26578                 if (maxDistance < 0.0001 &&
26579                     // Allow straightening even if already straight in order to remove extraneous nodes
26580                     keepingAllNodes) {
26581                     return 'straight_enough';
26582                 }
26583             };
26584
26585             action.transitionable = true;
26586
26587
26588             return action;
26589         }
26590
26591         // `actionUnrestrictTurn` deletes a turn restriction relation.
26592         //
26593         // `turn` must be an `osmTurn` object with a `restrictionID` property.
26594         // see osm/intersection.js, pathToTurn()
26595         //
26596         function actionUnrestrictTurn(turn) {
26597             return function(graph) {
26598                 return actionDeleteRelation(turn.restrictionID)(graph);
26599             };
26600         }
26601
26602         /* Reflect the given area around its axis of symmetry */
26603         function actionReflect(reflectIds, projection) {
26604             var _useLongAxis = true;
26605
26606
26607             var action = function(graph, t) {
26608                 if (t === null || !isFinite(t)) t = 1;
26609                 t = Math.min(Math.max(+t, 0), 1);
26610
26611                 var nodes = utilGetAllNodes(reflectIds, graph);
26612                 var points = nodes.map(function(n) { return projection(n.loc); });
26613                 var ssr = geoGetSmallestSurroundingRectangle(points);
26614
26615                 // Choose line pq = axis of symmetry.
26616                 // The shape's surrounding rectangle has 2 axes of symmetry.
26617                 // Reflect across the longer axis by default.
26618                 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2 ];
26619                 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2 ];
26620                 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2 ];
26621                 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2 ];
26622                 var p, q;
26623
26624                 var isLong = (geoVecLength(p1, q1) > geoVecLength(p2, q2));
26625                 if ((_useLongAxis && isLong) || (!_useLongAxis && !isLong)) {
26626                     p = p1;
26627                     q = q1;
26628                 } else {
26629                     p = p2;
26630                     q = q2;
26631                 }
26632
26633                 // reflect c across pq
26634                 // http://math.stackexchange.com/questions/65503/point-reflection-over-a-line
26635                 var dx = q[0] - p[0];
26636                 var dy = q[1] - p[1];
26637                 var a = (dx * dx - dy * dy) / (dx * dx + dy * dy);
26638                 var b = 2 * dx * dy / (dx * dx + dy * dy);
26639                 for (var i = 0; i < nodes.length; i++) {
26640                     var node = nodes[i];
26641                     var c = projection(node.loc);
26642                     var c2 = [
26643                         a * (c[0] - p[0]) + b * (c[1] - p[1]) + p[0],
26644                         b * (c[0] - p[0]) - a * (c[1] - p[1]) + p[1]
26645                     ];
26646                     var loc2 = projection.invert(c2);
26647                     node = node.move(geoVecInterp(node.loc, loc2, t));
26648                     graph = graph.replace(node);
26649                 }
26650
26651                 return graph;
26652             };
26653
26654
26655             action.useLongAxis = function(val) {
26656                 if (!arguments.length) return _useLongAxis;
26657                 _useLongAxis = val;
26658                 return action;
26659             };
26660
26661
26662             action.transitionable = true;
26663
26664
26665             return action;
26666         }
26667
26668         function actionUpgradeTags(entityId, oldTags, replaceTags) {
26669
26670             return function(graph) {
26671                 var entity = graph.entity(entityId);
26672                 var tags = Object.assign({}, entity.tags);  // shallow copy
26673                 var transferValue;
26674                 var semiIndex;
26675
26676                 for (var oldTagKey in oldTags) {
26677                     if (oldTags[oldTagKey] === '*') {
26678                         transferValue = tags[oldTagKey];
26679                         delete tags[oldTagKey];
26680                     } else {
26681                         var vals = tags[oldTagKey].split(';').filter(Boolean);
26682                         var oldIndex = vals.indexOf(oldTags[oldTagKey]);
26683                         if (vals.length === 1 || oldIndex === -1) {
26684                             delete tags[oldTagKey];
26685                         } else {
26686                             if (replaceTags && replaceTags[oldTagKey]) {
26687                                 // replacing a value within a semicolon-delimited value, note the index
26688                                 semiIndex = oldIndex;
26689                             }
26690                             vals.splice(oldIndex, 1);
26691                             tags[oldTagKey] = vals.join(';');
26692                         }
26693                     }
26694                 }
26695
26696                 if (replaceTags) {
26697                     for (var replaceKey in replaceTags) {
26698                         var replaceValue = replaceTags[replaceKey];
26699                         if (replaceValue === '*') {
26700                             if (tags[replaceKey] && tags[replaceKey] !== 'no') {
26701                                 // allow any pre-existing value except `no` (troll tag)
26702                                 continue;
26703                             } else {
26704                                 // otherwise assume `yes` is okay
26705                                 tags[replaceKey] = 'yes';
26706                             }
26707                         } else if (replaceValue === '$1') {
26708                             tags[replaceKey] = transferValue;
26709                         } else {
26710                             if (tags[replaceKey] && oldTags[replaceKey] && semiIndex !== undefined) {
26711                                 // don't override preexisting values
26712                                 var existingVals = tags[replaceKey].split(';').filter(Boolean);
26713                                 if (existingVals.indexOf(replaceValue) === -1) {
26714                                     existingVals.splice(semiIndex, 0, replaceValue);
26715                                     tags[replaceKey] = existingVals.join(';');
26716                                 }
26717                             } else {
26718                                 tags[replaceKey] = replaceValue;
26719                             }
26720                         }
26721                     }
26722                 }
26723
26724                 return graph.replace(entity.update({ tags: tags }));
26725             };
26726         }
26727
26728         function behaviorEdit(context) {
26729
26730             function behavior() {
26731                 context.map()
26732                     .minzoom(context.minEditableZoom());
26733             }
26734
26735
26736             behavior.off = function() {
26737                 context.map()
26738                     .minzoom(0);
26739             };
26740
26741             return behavior;
26742         }
26743
26744         /*
26745            The hover behavior adds the `.hover` class on pointerover to all elements to which
26746            the identical datum is bound, and removes it on pointerout.
26747
26748            The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
26749            representation may consist of several elements scattered throughout the DOM hierarchy.
26750            Only one of these elements can have the :hover pseudo-class, but all of them will
26751            have the .hover class.
26752          */
26753         function behaviorHover(context) {
26754             var dispatch$1 = dispatch('hover');
26755             var _selection = select(null);
26756             var _newNodeId = null;
26757             var _initialNodeID = null;
26758             var _altDisables;
26759             var _ignoreVertex;
26760             var _targets = [];
26761
26762             // use pointer events on supported platforms; fallback to mouse events
26763             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
26764
26765
26766             function keydown() {
26767                 if (_altDisables && event.keyCode === utilKeybinding.modifierCodes.alt) {
26768                     _selection.selectAll('.hover')
26769                         .classed('hover-suppressed', true)
26770                         .classed('hover', false);
26771
26772                     _selection
26773                         .classed('hover-disabled', true);
26774
26775                     dispatch$1.call('hover', this, null);
26776                 }
26777             }
26778
26779
26780             function keyup() {
26781                 if (_altDisables && event.keyCode === utilKeybinding.modifierCodes.alt) {
26782                     _selection.selectAll('.hover-suppressed')
26783                         .classed('hover-suppressed', false)
26784                         .classed('hover', true);
26785
26786                     _selection
26787                         .classed('hover-disabled', false);
26788
26789                     dispatch$1.call('hover', this, _targets);
26790                 }
26791             }
26792
26793
26794             function behavior(selection) {
26795                 _selection = selection;
26796
26797                 _targets = [];
26798
26799                 if (_initialNodeID) {
26800                     _newNodeId = _initialNodeID;
26801                     _initialNodeID = null;
26802                 } else {
26803                     _newNodeId = null;
26804                 }
26805
26806                 _selection
26807                     .on(_pointerPrefix + 'over.hover', pointerover)
26808                     .on(_pointerPrefix + 'out.hover', pointerout)
26809                     // treat pointerdown as pointerover for touch devices
26810                     .on(_pointerPrefix + 'down.hover', pointerover);
26811
26812                 select(window)
26813                     .on(_pointerPrefix + 'up.hover pointercancel.hover', pointerout, true)
26814                     .on('keydown.hover', keydown)
26815                     .on('keyup.hover', keyup);
26816
26817
26818                 function eventTarget() {
26819                     var datum = event.target && event.target.__data__;
26820                     if (typeof datum !== 'object') return null;
26821                     if (!(datum instanceof osmEntity) && datum.properties && (datum.properties.entity instanceof osmEntity)) {
26822                         return datum.properties.entity;
26823                     }
26824                     return datum;
26825                 }
26826
26827                 function pointerover() {
26828                     // ignore mouse hovers with buttons pressed unless dragging
26829                     if (context.mode().id.indexOf('drag') === -1 &&
26830                         (!event.pointerType || event.pointerType === 'mouse') &&
26831                         event.buttons) return;
26832
26833                     var target = eventTarget();
26834                     if (target && _targets.indexOf(target) === -1) {
26835                         _targets.push(target);
26836                         updateHover(_targets);
26837                     }
26838                 }
26839
26840                 function pointerout() {
26841
26842                     var target = eventTarget();
26843                     var index = _targets.indexOf(target);
26844                     if (index !== -1) {
26845                         _targets.splice(index);
26846                         updateHover(_targets);
26847                     }
26848                 }
26849
26850                 function allowsVertex(d) {
26851                     return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
26852                 }
26853
26854                 function modeAllowsHover(target) {
26855                     var mode = context.mode();
26856                     if (mode.id === 'add-point') {
26857                         return mode.preset.matchGeometry('vertex') ||
26858                             (target.type !== 'way' && target.geometry(context.graph()) !== 'vertex');
26859                     }
26860                     return true;
26861                 }
26862
26863                 function updateHover(targets) {
26864
26865                     _selection.selectAll('.hover')
26866                         .classed('hover', false);
26867                     _selection.selectAll('.hover-suppressed')
26868                         .classed('hover-suppressed', false);
26869
26870                     var mode = context.mode();
26871
26872                     if (!_newNodeId && (mode.id === 'draw-line' || mode.id === 'draw-area')) {
26873                         var node = targets.find(function(target) {
26874                             return target instanceof osmEntity && target.type === 'node';
26875                         });
26876                         _newNodeId = node && node.id;
26877                     }
26878
26879                     targets = targets.filter(function(datum) {
26880                         if (datum instanceof osmEntity) {
26881                             // If drawing a way, don't hover on a node that was just placed. #3974
26882                             return datum.id !== _newNodeId &&
26883                                 (datum.type !== 'node' || !_ignoreVertex || allowsVertex(datum)) &&
26884                                 modeAllowsHover(datum);
26885                         }
26886                         return true;
26887                     });
26888
26889                     var selector = '';
26890
26891                     for (var i in targets) {
26892                         var datum = targets[i];
26893
26894                         // What are we hovering over?
26895                         if (datum.__featurehash__) {
26896                             // hovering custom data
26897                             selector += ', .data' + datum.__featurehash__;
26898
26899                         } else if (datum instanceof QAItem) {
26900                             selector += ', .' + datum.service + '.itemId-' + datum.id;
26901
26902                         } else if (datum instanceof osmNote) {
26903                             selector += ', .note-' + datum.id;
26904
26905                         } else if (datum instanceof osmEntity) {
26906                             selector += ', .' + datum.id;
26907                             if (datum.type === 'relation') {
26908                                 for (var j in datum.members) {
26909                                     selector += ', .' + datum.members[j].id;
26910                                 }
26911                             }
26912                         }
26913                     }
26914
26915                     var suppressed = _altDisables && event && event.altKey;
26916
26917                     if (selector.trim().length) {
26918                         // remove the first comma
26919                         selector = selector.slice(1);
26920                         _selection.selectAll(selector)
26921                             .classed(suppressed ? 'hover-suppressed' : 'hover', true);
26922                     }
26923
26924                     dispatch$1.call('hover', this, !suppressed && targets);
26925                 }
26926             }
26927
26928
26929             behavior.off = function(selection) {
26930                 selection.selectAll('.hover')
26931                     .classed('hover', false);
26932                 selection.selectAll('.hover-suppressed')
26933                     .classed('hover-suppressed', false);
26934                 selection
26935                     .classed('hover-disabled', false);
26936
26937                 selection
26938                     .on(_pointerPrefix + 'over.hover', null)
26939                     .on(_pointerPrefix + 'out.hover', null)
26940                     .on(_pointerPrefix + 'down.hover', null);
26941
26942                 select(window)
26943                     .on(_pointerPrefix + 'up.hover pointercancel.hover', null, true)
26944                     .on('keydown.hover', null)
26945                     .on('keyup.hover', null);
26946             };
26947
26948
26949             behavior.altDisables = function(val) {
26950                 if (!arguments.length) return _altDisables;
26951                 _altDisables = val;
26952                 return behavior;
26953             };
26954
26955             behavior.ignoreVertex = function(val) {
26956                 if (!arguments.length) return _ignoreVertex;
26957                 _ignoreVertex = val;
26958                 return behavior;
26959             };
26960
26961             behavior.initialNodeID = function(nodeId) {
26962                 _initialNodeID = nodeId;
26963                 return behavior;
26964             };
26965
26966             return utilRebind(behavior, dispatch$1, 'on');
26967         }
26968
26969         var _disableSpace = false;
26970         var _lastSpace = null;
26971
26972
26973         function behaviorDraw(context) {
26974             var dispatch$1 = dispatch(
26975                 'move', 'down', 'downcancel', 'click', 'clickWay', 'clickNode', 'undo', 'cancel', 'finish'
26976             );
26977
26978             var keybinding = utilKeybinding('draw');
26979
26980             var _hover = behaviorHover(context)
26981                 .altDisables(true)
26982                 .ignoreVertex(true)
26983                 .on('hover', context.ui().sidebar.hover);
26984             var _edit = behaviorEdit(context);
26985
26986             var _closeTolerance = 4;
26987             var _tolerance = 12;
26988             var _mouseLeave = false;
26989             var _lastMouse = null;
26990             var _lastPointerUpEvent;
26991
26992             var _downPointer;
26993
26994             // use pointer events on supported platforms; fallback to mouse events
26995             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
26996
26997
26998             // related code
26999             // - `mode/drag_node.js` `datum()`
27000             function datum() {
27001                 var mode = context.mode();
27002                 var isNote = mode && (mode.id.indexOf('note') !== -1);
27003                 if (event.altKey || isNote) return {};
27004
27005                 var element;
27006                 if (event.type === 'keydown') {
27007                     element = _lastMouse && _lastMouse.target;
27008                 } else {
27009                     element = event.target;
27010                 }
27011
27012                 // When drawing, snap only to touch targets..
27013                 // (this excludes area fills and active drawing elements)
27014                 var d = element.__data__;
27015                 return (d && d.properties && d.properties.target) ? d : {};
27016             }
27017
27018             function pointerdown() {
27019
27020                 if (_downPointer) return;
27021
27022                 var pointerLocGetter = utilFastMouse(this);
27023                 _downPointer = {
27024                     id: event.pointerId || 'mouse',
27025                     pointerLocGetter: pointerLocGetter,
27026                     downTime: +new Date(),
27027                     downLoc: pointerLocGetter(event)
27028                 };
27029
27030                 dispatch$1.call('down', this, datum());
27031             }
27032
27033             function pointerup() {
27034
27035                 if (!_downPointer || _downPointer.id !== (event.pointerId || 'mouse')) return;
27036
27037                 var downPointer = _downPointer;
27038                 _downPointer = null;
27039
27040                 _lastPointerUpEvent = event;
27041
27042                 if (downPointer.isCancelled) return;
27043
27044                 var t2 = +new Date();
27045                 var p2 = downPointer.pointerLocGetter(event);
27046                 var dist = geoVecLength(downPointer.downLoc, p2);
27047
27048                 if (dist < _closeTolerance || (dist < _tolerance && (t2 - downPointer.downTime) < 500)) {
27049                     // Prevent a quick second click
27050                     select(window).on('click.draw-block', function() {
27051                         event.stopPropagation();
27052                     }, true);
27053
27054                     context.map().dblclickZoomEnable(false);
27055
27056                     window.setTimeout(function() {
27057                         context.map().dblclickZoomEnable(true);
27058                         select(window).on('click.draw-block', null);
27059                     }, 500);
27060
27061                     click(p2);
27062                 }
27063             }
27064
27065             function pointermove() {
27066                 if (_downPointer &&
27067                     _downPointer.id === (event.pointerId || 'mouse') &&
27068                     !_downPointer.isCancelled) {
27069                     var p2 = _downPointer.pointerLocGetter(event);
27070                     var dist = geoVecLength(_downPointer.downLoc, p2);
27071                     if (dist >= _closeTolerance) {
27072                         _downPointer.isCancelled = true;
27073                         dispatch$1.call('downcancel', this);
27074                     }
27075                 }
27076
27077                 if ((event.pointerType && event.pointerType !== 'mouse') ||
27078                     event.buttons ||
27079                     _downPointer) return;
27080
27081                 // HACK: Mobile Safari likes to send one or more `mouse` type pointermove
27082                 // events immediately after non-mouse pointerup events; detect and ignore them.
27083                 if (_lastPointerUpEvent &&
27084                     _lastPointerUpEvent.pointerType !== 'mouse' &&
27085                     event.timeStamp - _lastPointerUpEvent.timeStamp < 100) return;
27086
27087                 _lastMouse = event;
27088                 dispatch$1.call('move', this, datum());
27089             }
27090
27091             function pointercancel() {
27092                 if (_downPointer &&
27093                     _downPointer.id === (event.pointerId || 'mouse')) {
27094
27095                     if (!_downPointer.isCancelled) {
27096                         dispatch$1.call('downcancel', this);
27097                     }
27098                     _downPointer = null;
27099                 }
27100             }
27101
27102             function mouseenter() {
27103                 _mouseLeave = false;
27104             }
27105
27106             function mouseleave() {
27107                 _mouseLeave = true;
27108             }
27109
27110             function allowsVertex(d) {
27111                 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
27112             }
27113
27114             // related code
27115             // - `mode/drag_node.js`     `doMove()`
27116             // - `behavior/draw.js`      `click()`
27117             // - `behavior/draw_way.js`  `move()`
27118             function click(loc) {
27119                 var d = datum();
27120                 var target = d && d.properties && d.properties.entity;
27121
27122                 var mode = context.mode();
27123
27124                 if (target && target.type === 'node' && allowsVertex(target)) {   // Snap to a node
27125                     dispatch$1.call('clickNode', this, target, d);
27126                     return;
27127
27128                 } else if (target && target.type === 'way' && (mode.id !== 'add-point' || mode.preset.matchGeometry('vertex'))) {   // Snap to a way
27129                     var choice = geoChooseEdge(
27130                         context.graph().childNodes(target), loc, context.projection, context.activeID()
27131                     );
27132                     if (choice) {
27133                         var edge = [target.nodes[choice.index - 1], target.nodes[choice.index]];
27134                         dispatch$1.call('clickWay', this, choice.loc, edge, d);
27135                         return;
27136                     }
27137                 } else if (mode.id !== 'add-point' || mode.preset.matchGeometry('point')) {
27138                     var locLatLng = context.projection.invert(loc);
27139                     dispatch$1.call('click', this, locLatLng, d);
27140                 }
27141
27142             }
27143
27144             // treat a spacebar press like a click
27145             function space() {
27146                 event.preventDefault();
27147                 event.stopPropagation();
27148
27149                 var currSpace = context.map().mouse();
27150                 if (_disableSpace && _lastSpace) {
27151                     var dist = geoVecLength(_lastSpace, currSpace);
27152                     if (dist > _tolerance) {
27153                         _disableSpace = false;
27154                     }
27155                 }
27156
27157                 if (_disableSpace || _mouseLeave || !_lastMouse) return;
27158
27159                 // user must move mouse or release space bar to allow another click
27160                 _lastSpace = currSpace;
27161                 _disableSpace = true;
27162
27163                 select(window).on('keyup.space-block', function() {
27164                     event.preventDefault();
27165                     event.stopPropagation();
27166                     _disableSpace = false;
27167                     select(window).on('keyup.space-block', null);
27168                 });
27169
27170                 // get the current mouse position
27171                 var loc = context.map().mouse() ||
27172                     // or the map center if the mouse has never entered the map
27173                     context.projection(context.map().center());
27174                 click(loc);
27175             }
27176
27177
27178             function backspace() {
27179                 event.preventDefault();
27180                 dispatch$1.call('undo');
27181             }
27182
27183
27184             function del() {
27185                 event.preventDefault();
27186                 dispatch$1.call('cancel');
27187             }
27188
27189
27190             function ret() {
27191                 event.preventDefault();
27192                 dispatch$1.call('finish');
27193             }
27194
27195
27196             function behavior(selection) {
27197                 context.install(_hover);
27198                 context.install(_edit);
27199
27200                 _downPointer = null;
27201
27202                 keybinding
27203                     .on('⌫', backspace)
27204                     .on('⌦', del)
27205                     .on('⎋', ret)
27206                     .on('↩', ret)
27207                     .on('space', space)
27208                     .on('⌥space', space);
27209
27210                 selection
27211                     .on('mouseenter.draw', mouseenter)
27212                     .on('mouseleave.draw', mouseleave)
27213                     .on(_pointerPrefix + 'down.draw', pointerdown)
27214                     .on(_pointerPrefix + 'move.draw', pointermove);
27215
27216                 select(window)
27217                     .on(_pointerPrefix + 'up.draw', pointerup, true)
27218                     .on('pointercancel.draw', pointercancel, true);
27219
27220                 select(document)
27221                     .call(keybinding);
27222
27223                 return behavior;
27224             }
27225
27226
27227             behavior.off = function(selection) {
27228                 context.ui().sidebar.hover.cancel();
27229                 context.uninstall(_hover);
27230                 context.uninstall(_edit);
27231
27232                 selection
27233                     .on('mouseenter.draw', null)
27234                     .on('mouseleave.draw', null)
27235                     .on(_pointerPrefix + 'down.draw', null)
27236                     .on(_pointerPrefix + 'move.draw', null);
27237
27238                 select(window)
27239                     .on(_pointerPrefix + 'up.draw', null)
27240                     .on('pointercancel.draw', null);
27241                     // note: keyup.space-block, click.draw-block should remain
27242
27243                 select(document)
27244                     .call(keybinding.unbind);
27245             };
27246
27247
27248             behavior.hover = function() {
27249                 return _hover;
27250             };
27251
27252
27253             return utilRebind(behavior, dispatch$1, 'on');
27254         }
27255
27256         function initRange(domain, range) {
27257           switch (arguments.length) {
27258             case 0: break;
27259             case 1: this.range(domain); break;
27260             default: this.range(range).domain(domain); break;
27261           }
27262           return this;
27263         }
27264
27265         var prefix = "$";
27266
27267         function Map$1() {}
27268
27269         Map$1.prototype = map$2.prototype = {
27270           constructor: Map$1,
27271           has: function(key) {
27272             return (prefix + key) in this;
27273           },
27274           get: function(key) {
27275             return this[prefix + key];
27276           },
27277           set: function(key, value) {
27278             this[prefix + key] = value;
27279             return this;
27280           },
27281           remove: function(key) {
27282             var property = prefix + key;
27283             return property in this && delete this[property];
27284           },
27285           clear: function() {
27286             for (var property in this) if (property[0] === prefix) delete this[property];
27287           },
27288           keys: function() {
27289             var keys = [];
27290             for (var property in this) if (property[0] === prefix) keys.push(property.slice(1));
27291             return keys;
27292           },
27293           values: function() {
27294             var values = [];
27295             for (var property in this) if (property[0] === prefix) values.push(this[property]);
27296             return values;
27297           },
27298           entries: function() {
27299             var entries = [];
27300             for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]});
27301             return entries;
27302           },
27303           size: function() {
27304             var size = 0;
27305             for (var property in this) if (property[0] === prefix) ++size;
27306             return size;
27307           },
27308           empty: function() {
27309             for (var property in this) if (property[0] === prefix) return false;
27310             return true;
27311           },
27312           each: function(f) {
27313             for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this);
27314           }
27315         };
27316
27317         function map$2(object, f) {
27318           var map = new Map$1;
27319
27320           // Copy constructor.
27321           if (object instanceof Map$1) object.each(function(value, key) { map.set(key, value); });
27322
27323           // Index array by numeric index or specified key function.
27324           else if (Array.isArray(object)) {
27325             var i = -1,
27326                 n = object.length,
27327                 o;
27328
27329             if (f == null) while (++i < n) map.set(i, object[i]);
27330             else while (++i < n) map.set(f(o = object[i], i, object), o);
27331           }
27332
27333           // Convert object to map.
27334           else if (object) for (var key in object) map.set(key, object[key]);
27335
27336           return map;
27337         }
27338
27339         function Set$1() {}
27340
27341         var proto = map$2.prototype;
27342
27343         Set$1.prototype = set$2.prototype = {
27344           constructor: Set$1,
27345           has: proto.has,
27346           add: function(value) {
27347             value += "";
27348             this[prefix + value] = value;
27349             return this;
27350           },
27351           remove: proto.remove,
27352           clear: proto.clear,
27353           values: proto.keys,
27354           size: proto.size,
27355           empty: proto.empty,
27356           each: proto.each
27357         };
27358
27359         function set$2(object, f) {
27360           var set = new Set$1;
27361
27362           // Copy constructor.
27363           if (object instanceof Set$1) object.each(function(value) { set.add(value); });
27364
27365           // Otherwise, assume it’s an array.
27366           else if (object) {
27367             var i = -1, n = object.length;
27368             if (f == null) while (++i < n) set.add(object[i]);
27369             else while (++i < n) set.add(f(object[i], i, object));
27370           }
27371
27372           return set;
27373         }
27374
27375         var array$1 = Array.prototype;
27376
27377         var map$3 = array$1.map;
27378         var slice$4 = array$1.slice;
27379
27380         function constant$4(x) {
27381           return function() {
27382             return x;
27383           };
27384         }
27385
27386         function number$1(x) {
27387           return +x;
27388         }
27389
27390         var unit = [0, 1];
27391
27392         function identity$3(x) {
27393           return x;
27394         }
27395
27396         function normalize(a, b) {
27397           return (b -= (a = +a))
27398               ? function(x) { return (x - a) / b; }
27399               : constant$4(isNaN(b) ? NaN : 0.5);
27400         }
27401
27402         function clamper(domain) {
27403           var a = domain[0], b = domain[domain.length - 1], t;
27404           if (a > b) t = a, a = b, b = t;
27405           return function(x) { return Math.max(a, Math.min(b, x)); };
27406         }
27407
27408         // normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
27409         // interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b].
27410         function bimap(domain, range, interpolate) {
27411           var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1];
27412           if (d1 < d0) d0 = normalize(d1, d0), r0 = interpolate(r1, r0);
27413           else d0 = normalize(d0, d1), r0 = interpolate(r0, r1);
27414           return function(x) { return r0(d0(x)); };
27415         }
27416
27417         function polymap(domain, range, interpolate) {
27418           var j = Math.min(domain.length, range.length) - 1,
27419               d = new Array(j),
27420               r = new Array(j),
27421               i = -1;
27422
27423           // Reverse descending domains.
27424           if (domain[j] < domain[0]) {
27425             domain = domain.slice().reverse();
27426             range = range.slice().reverse();
27427           }
27428
27429           while (++i < j) {
27430             d[i] = normalize(domain[i], domain[i + 1]);
27431             r[i] = interpolate(range[i], range[i + 1]);
27432           }
27433
27434           return function(x) {
27435             var i = bisectRight(domain, x, 1, j) - 1;
27436             return r[i](d[i](x));
27437           };
27438         }
27439
27440         function copy$1(source, target) {
27441           return target
27442               .domain(source.domain())
27443               .range(source.range())
27444               .interpolate(source.interpolate())
27445               .clamp(source.clamp())
27446               .unknown(source.unknown());
27447         }
27448
27449         function transformer$1() {
27450           var domain = unit,
27451               range = unit,
27452               interpolate$1 = interpolate,
27453               transform,
27454               untransform,
27455               unknown,
27456               clamp = identity$3,
27457               piecewise,
27458               output,
27459               input;
27460
27461           function rescale() {
27462             piecewise = Math.min(domain.length, range.length) > 2 ? polymap : bimap;
27463             output = input = null;
27464             return scale;
27465           }
27466
27467           function scale(x) {
27468             return isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate$1)))(transform(clamp(x)));
27469           }
27470
27471           scale.invert = function(y) {
27472             return clamp(untransform((input || (input = piecewise(range, domain.map(transform), d3_interpolateNumber)))(y)));
27473           };
27474
27475           scale.domain = function(_) {
27476             return arguments.length ? (domain = map$3.call(_, number$1), clamp === identity$3 || (clamp = clamper(domain)), rescale()) : domain.slice();
27477           };
27478
27479           scale.range = function(_) {
27480             return arguments.length ? (range = slice$4.call(_), rescale()) : range.slice();
27481           };
27482
27483           scale.rangeRound = function(_) {
27484             return range = slice$4.call(_), interpolate$1 = interpolateRound, rescale();
27485           };
27486
27487           scale.clamp = function(_) {
27488             return arguments.length ? (clamp = _ ? clamper(domain) : identity$3, scale) : clamp !== identity$3;
27489           };
27490
27491           scale.interpolate = function(_) {
27492             return arguments.length ? (interpolate$1 = _, rescale()) : interpolate$1;
27493           };
27494
27495           scale.unknown = function(_) {
27496             return arguments.length ? (unknown = _, scale) : unknown;
27497           };
27498
27499           return function(t, u) {
27500             transform = t, untransform = u;
27501             return rescale();
27502           };
27503         }
27504
27505         function continuous(transform, untransform) {
27506           return transformer$1()(transform, untransform);
27507         }
27508
27509         // Computes the decimal coefficient and exponent of the specified number x with
27510         // significant digits p, where x is positive and p is in [1, 21] or undefined.
27511         // For example, formatDecimal(1.23) returns ["123", 0].
27512         function formatDecimal(x, p) {
27513           if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
27514           var i, coefficient = x.slice(0, i);
27515
27516           // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
27517           // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
27518           return [
27519             coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,
27520             +x.slice(i + 1)
27521           ];
27522         }
27523
27524         function exponent(x) {
27525           return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN;
27526         }
27527
27528         function formatGroup(grouping, thousands) {
27529           return function(value, width) {
27530             var i = value.length,
27531                 t = [],
27532                 j = 0,
27533                 g = grouping[0],
27534                 length = 0;
27535
27536             while (i > 0 && g > 0) {
27537               if (length + g + 1 > width) g = Math.max(1, width - length);
27538               t.push(value.substring(i -= g, i + g));
27539               if ((length += g + 1) > width) break;
27540               g = grouping[j = (j + 1) % grouping.length];
27541             }
27542
27543             return t.reverse().join(thousands);
27544           };
27545         }
27546
27547         function formatNumerals(numerals) {
27548           return function(value) {
27549             return value.replace(/[0-9]/g, function(i) {
27550               return numerals[+i];
27551             });
27552           };
27553         }
27554
27555         // [[fill]align][sign][symbol][0][width][,][.precision][~][type]
27556         var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
27557
27558         function formatSpecifier(specifier) {
27559           if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
27560           var match;
27561           return new FormatSpecifier({
27562             fill: match[1],
27563             align: match[2],
27564             sign: match[3],
27565             symbol: match[4],
27566             zero: match[5],
27567             width: match[6],
27568             comma: match[7],
27569             precision: match[8] && match[8].slice(1),
27570             trim: match[9],
27571             type: match[10]
27572           });
27573         }
27574
27575         formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
27576
27577         function FormatSpecifier(specifier) {
27578           this.fill = specifier.fill === undefined ? " " : specifier.fill + "";
27579           this.align = specifier.align === undefined ? ">" : specifier.align + "";
27580           this.sign = specifier.sign === undefined ? "-" : specifier.sign + "";
27581           this.symbol = specifier.symbol === undefined ? "" : specifier.symbol + "";
27582           this.zero = !!specifier.zero;
27583           this.width = specifier.width === undefined ? undefined : +specifier.width;
27584           this.comma = !!specifier.comma;
27585           this.precision = specifier.precision === undefined ? undefined : +specifier.precision;
27586           this.trim = !!specifier.trim;
27587           this.type = specifier.type === undefined ? "" : specifier.type + "";
27588         }
27589
27590         FormatSpecifier.prototype.toString = function() {
27591           return this.fill
27592               + this.align
27593               + this.sign
27594               + this.symbol
27595               + (this.zero ? "0" : "")
27596               + (this.width === undefined ? "" : Math.max(1, this.width | 0))
27597               + (this.comma ? "," : "")
27598               + (this.precision === undefined ? "" : "." + Math.max(0, this.precision | 0))
27599               + (this.trim ? "~" : "")
27600               + this.type;
27601         };
27602
27603         // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
27604         function formatTrim(s) {
27605           out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
27606             switch (s[i]) {
27607               case ".": i0 = i1 = i; break;
27608               case "0": if (i0 === 0) i0 = i; i1 = i; break;
27609               default: if (!+s[i]) break out; if (i0 > 0) i0 = 0; break;
27610             }
27611           }
27612           return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
27613         }
27614
27615         var prefixExponent;
27616
27617         function formatPrefixAuto(x, p) {
27618           var d = formatDecimal(x, p);
27619           if (!d) return x + "";
27620           var coefficient = d[0],
27621               exponent = d[1],
27622               i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
27623               n = coefficient.length;
27624           return i === n ? coefficient
27625               : i > n ? coefficient + new Array(i - n + 1).join("0")
27626               : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i)
27627               : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y!
27628         }
27629
27630         function formatRounded(x, p) {
27631           var d = formatDecimal(x, p);
27632           if (!d) return x + "";
27633           var coefficient = d[0],
27634               exponent = d[1];
27635           return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient
27636               : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1)
27637               : coefficient + new Array(exponent - coefficient.length + 2).join("0");
27638         }
27639
27640         var formatTypes = {
27641           "%": function(x, p) { return (x * 100).toFixed(p); },
27642           "b": function(x) { return Math.round(x).toString(2); },
27643           "c": function(x) { return x + ""; },
27644           "d": function(x) { return Math.round(x).toString(10); },
27645           "e": function(x, p) { return x.toExponential(p); },
27646           "f": function(x, p) { return x.toFixed(p); },
27647           "g": function(x, p) { return x.toPrecision(p); },
27648           "o": function(x) { return Math.round(x).toString(8); },
27649           "p": function(x, p) { return formatRounded(x * 100, p); },
27650           "r": formatRounded,
27651           "s": formatPrefixAuto,
27652           "X": function(x) { return Math.round(x).toString(16).toUpperCase(); },
27653           "x": function(x) { return Math.round(x).toString(16); }
27654         };
27655
27656         function identity$4(x) {
27657           return x;
27658         }
27659
27660         var map$4 = Array.prototype.map,
27661             prefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];
27662
27663         function formatLocale(locale) {
27664           var group = locale.grouping === undefined || locale.thousands === undefined ? identity$4 : formatGroup(map$4.call(locale.grouping, Number), locale.thousands + ""),
27665               currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "",
27666               currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "",
27667               decimal = locale.decimal === undefined ? "." : locale.decimal + "",
27668               numerals = locale.numerals === undefined ? identity$4 : formatNumerals(map$4.call(locale.numerals, String)),
27669               percent = locale.percent === undefined ? "%" : locale.percent + "",
27670               minus = locale.minus === undefined ? "-" : locale.minus + "",
27671               nan = locale.nan === undefined ? "NaN" : locale.nan + "";
27672
27673           function newFormat(specifier) {
27674             specifier = formatSpecifier(specifier);
27675
27676             var fill = specifier.fill,
27677                 align = specifier.align,
27678                 sign = specifier.sign,
27679                 symbol = specifier.symbol,
27680                 zero = specifier.zero,
27681                 width = specifier.width,
27682                 comma = specifier.comma,
27683                 precision = specifier.precision,
27684                 trim = specifier.trim,
27685                 type = specifier.type;
27686
27687             // The "n" type is an alias for ",g".
27688             if (type === "n") comma = true, type = "g";
27689
27690             // The "" type, and any invalid type, is an alias for ".12~g".
27691             else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = "g";
27692
27693             // If zero fill is specified, padding goes after sign and before digits.
27694             if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "=";
27695
27696             // Compute the prefix and suffix.
27697             // For SI-prefix, the suffix is lazily computed.
27698             var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
27699                 suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : "";
27700
27701             // What format function should we use?
27702             // Is this an integer type?
27703             // Can this type generate exponential notation?
27704             var formatType = formatTypes[type],
27705                 maybeSuffix = /[defgprs%]/.test(type);
27706
27707             // Set the default precision if not specified,
27708             // or clamp the specified precision to the supported range.
27709             // For significant precision, it must be in [1, 21].
27710             // For fixed precision, it must be in [0, 20].
27711             precision = precision === undefined ? 6
27712                 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))
27713                 : Math.max(0, Math.min(20, precision));
27714
27715             function format(value) {
27716               var valuePrefix = prefix,
27717                   valueSuffix = suffix,
27718                   i, n, c;
27719
27720               if (type === "c") {
27721                 valueSuffix = formatType(value) + valueSuffix;
27722                 value = "";
27723               } else {
27724                 value = +value;
27725
27726                 // Determine the sign. -0 is not less than 0, but 1 / -0 is!
27727                 var valueNegative = value < 0 || 1 / value < 0;
27728
27729                 // Perform the initial formatting.
27730                 value = isNaN(value) ? nan : formatType(Math.abs(value), precision);
27731
27732                 // Trim insignificant zeros.
27733                 if (trim) value = formatTrim(value);
27734
27735                 // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign.
27736                 if (valueNegative && +value === 0 && sign !== "+") valueNegative = false;
27737
27738                 // Compute the prefix and suffix.
27739                 valuePrefix = (valueNegative ? (sign === "(" ? sign : minus) : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
27740                 valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : "");
27741
27742                 // Break the formatted value into the integer “value” part that can be
27743                 // grouped, and fractional or exponential “suffix” part that is not.
27744                 if (maybeSuffix) {
27745                   i = -1, n = value.length;
27746                   while (++i < n) {
27747                     if (c = value.charCodeAt(i), 48 > c || c > 57) {
27748                       valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
27749                       value = value.slice(0, i);
27750                       break;
27751                     }
27752                   }
27753                 }
27754               }
27755
27756               // If the fill character is not "0", grouping is applied before padding.
27757               if (comma && !zero) value = group(value, Infinity);
27758
27759               // Compute the padding.
27760               var length = valuePrefix.length + value.length + valueSuffix.length,
27761                   padding = length < width ? new Array(width - length + 1).join(fill) : "";
27762
27763               // If the fill character is "0", grouping is applied after padding.
27764               if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = "";
27765
27766               // Reconstruct the final output based on the desired alignment.
27767               switch (align) {
27768                 case "<": value = valuePrefix + value + valueSuffix + padding; break;
27769                 case "=": value = valuePrefix + padding + value + valueSuffix; break;
27770                 case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;
27771                 default: value = padding + valuePrefix + value + valueSuffix; break;
27772               }
27773
27774               return numerals(value);
27775             }
27776
27777             format.toString = function() {
27778               return specifier + "";
27779             };
27780
27781             return format;
27782           }
27783
27784           function formatPrefix(specifier, value) {
27785             var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
27786                 e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
27787                 k = Math.pow(10, -e),
27788                 prefix = prefixes[8 + e / 3];
27789             return function(value) {
27790               return f(k * value) + prefix;
27791             };
27792           }
27793
27794           return {
27795             format: newFormat,
27796             formatPrefix: formatPrefix
27797           };
27798         }
27799
27800         var locale;
27801         var format;
27802         var formatPrefix;
27803
27804         defaultLocale({
27805           decimal: ".",
27806           thousands: ",",
27807           grouping: [3],
27808           currency: ["$", ""],
27809           minus: "-"
27810         });
27811
27812         function defaultLocale(definition) {
27813           locale = formatLocale(definition);
27814           format = locale.format;
27815           formatPrefix = locale.formatPrefix;
27816           return locale;
27817         }
27818
27819         function precisionFixed(step) {
27820           return Math.max(0, -exponent(Math.abs(step)));
27821         }
27822
27823         function precisionPrefix(step, value) {
27824           return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
27825         }
27826
27827         function precisionRound(step, max) {
27828           step = Math.abs(step), max = Math.abs(max) - step;
27829           return Math.max(0, exponent(max) - exponent(step)) + 1;
27830         }
27831
27832         function tickFormat(start, stop, count, specifier) {
27833           var step = tickStep(start, stop, count),
27834               precision;
27835           specifier = formatSpecifier(specifier == null ? ",f" : specifier);
27836           switch (specifier.type) {
27837             case "s": {
27838               var value = Math.max(Math.abs(start), Math.abs(stop));
27839               if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
27840               return formatPrefix(specifier, value);
27841             }
27842             case "":
27843             case "e":
27844             case "g":
27845             case "p":
27846             case "r": {
27847               if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
27848               break;
27849             }
27850             case "f":
27851             case "%": {
27852               if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
27853               break;
27854             }
27855           }
27856           return format(specifier);
27857         }
27858
27859         function linearish(scale) {
27860           var domain = scale.domain;
27861
27862           scale.ticks = function(count) {
27863             var d = domain();
27864             return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
27865           };
27866
27867           scale.tickFormat = function(count, specifier) {
27868             var d = domain();
27869             return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier);
27870           };
27871
27872           scale.nice = function(count) {
27873             if (count == null) count = 10;
27874
27875             var d = domain(),
27876                 i0 = 0,
27877                 i1 = d.length - 1,
27878                 start = d[i0],
27879                 stop = d[i1],
27880                 step;
27881
27882             if (stop < start) {
27883               step = start, start = stop, stop = step;
27884               step = i0, i0 = i1, i1 = step;
27885             }
27886
27887             step = tickIncrement(start, stop, count);
27888
27889             if (step > 0) {
27890               start = Math.floor(start / step) * step;
27891               stop = Math.ceil(stop / step) * step;
27892               step = tickIncrement(start, stop, count);
27893             } else if (step < 0) {
27894               start = Math.ceil(start * step) / step;
27895               stop = Math.floor(stop * step) / step;
27896               step = tickIncrement(start, stop, count);
27897             }
27898
27899             if (step > 0) {
27900               d[i0] = Math.floor(start / step) * step;
27901               d[i1] = Math.ceil(stop / step) * step;
27902               domain(d);
27903             } else if (step < 0) {
27904               d[i0] = Math.ceil(start * step) / step;
27905               d[i1] = Math.floor(stop * step) / step;
27906               domain(d);
27907             }
27908
27909             return scale;
27910           };
27911
27912           return scale;
27913         }
27914
27915         function linear$2() {
27916           var scale = continuous(identity$3, identity$3);
27917
27918           scale.copy = function() {
27919             return copy$1(scale, linear$2());
27920           };
27921
27922           initRange.apply(scale, arguments);
27923
27924           return linearish(scale);
27925         }
27926
27927         function quantize() {
27928           var x0 = 0,
27929               x1 = 1,
27930               n = 1,
27931               domain = [0.5],
27932               range = [0, 1],
27933               unknown;
27934
27935           function scale(x) {
27936             return x <= x ? range[bisectRight(domain, x, 0, n)] : unknown;
27937           }
27938
27939           function rescale() {
27940             var i = -1;
27941             domain = new Array(n);
27942             while (++i < n) domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1);
27943             return scale;
27944           }
27945
27946           scale.domain = function(_) {
27947             return arguments.length ? (x0 = +_[0], x1 = +_[1], rescale()) : [x0, x1];
27948           };
27949
27950           scale.range = function(_) {
27951             return arguments.length ? (n = (range = slice$4.call(_)).length - 1, rescale()) : range.slice();
27952           };
27953
27954           scale.invertExtent = function(y) {
27955             var i = range.indexOf(y);
27956             return i < 0 ? [NaN, NaN]
27957                 : i < 1 ? [x0, domain[0]]
27958                 : i >= n ? [domain[n - 1], x1]
27959                 : [domain[i - 1], domain[i]];
27960           };
27961
27962           scale.unknown = function(_) {
27963             return arguments.length ? (unknown = _, scale) : scale;
27964           };
27965
27966           scale.thresholds = function() {
27967             return domain.slice();
27968           };
27969
27970           scale.copy = function() {
27971             return quantize()
27972                 .domain([x0, x1])
27973                 .range(range)
27974                 .unknown(unknown);
27975           };
27976
27977           return initRange.apply(linearish(scale), arguments);
27978         }
27979
27980         function behaviorBreathe() {
27981             var duration = 800;
27982             var steps = 4;
27983             var selector = '.selected.shadow, .selected .shadow';
27984             var _selected = select(null);
27985             var _classed = '';
27986             var _params = {};
27987             var _done = false;
27988             var _timer;
27989
27990
27991             function ratchetyInterpolator(a, b, steps, units) {
27992                 a = parseFloat(a);
27993                 b = parseFloat(b);
27994                 var sample = quantize()
27995                     .domain([0, 1])
27996                     .range(d3_quantize(d3_interpolateNumber(a, b), steps));
27997
27998                 return function(t) {
27999                     return String(sample(t)) + (units || '');
28000                 };
28001             }
28002
28003
28004             function reset(selection) {
28005                 selection
28006                     .style('stroke-opacity', null)
28007                     .style('stroke-width', null)
28008                     .style('fill-opacity', null)
28009                     .style('r', null);
28010             }
28011
28012
28013             function setAnimationParams(transition, fromTo) {
28014                 var toFrom = (fromTo === 'from' ? 'to' : 'from');
28015
28016                 transition
28017                     .styleTween('stroke-opacity', function(d) {
28018                         return ratchetyInterpolator(
28019                             _params[d.id][toFrom].opacity,
28020                             _params[d.id][fromTo].opacity,
28021                             steps
28022                         );
28023                     })
28024                     .styleTween('stroke-width', function(d) {
28025                         return ratchetyInterpolator(
28026                             _params[d.id][toFrom].width,
28027                             _params[d.id][fromTo].width,
28028                             steps,
28029                             'px'
28030                         );
28031                     })
28032                     .styleTween('fill-opacity', function(d) {
28033                         return ratchetyInterpolator(
28034                             _params[d.id][toFrom].opacity,
28035                             _params[d.id][fromTo].opacity,
28036                             steps
28037                         );
28038                     })
28039                     .styleTween('r', function(d) {
28040                         return ratchetyInterpolator(
28041                             _params[d.id][toFrom].width,
28042                             _params[d.id][fromTo].width,
28043                             steps,
28044                             'px'
28045                         );
28046                     });
28047             }
28048
28049
28050             function calcAnimationParams(selection) {
28051                 selection
28052                     .call(reset)
28053                     .each(function(d) {
28054                         var s = select(this);
28055                         var tag = s.node().tagName;
28056                         var p = {'from': {}, 'to': {}};
28057                         var opacity;
28058                         var width;
28059
28060                         // determine base opacity and width
28061                         if (tag === 'circle') {
28062                             opacity = parseFloat(s.style('fill-opacity') || 0.5);
28063                             width = parseFloat(s.style('r') || 15.5);
28064                         } else {
28065                             opacity = parseFloat(s.style('stroke-opacity') || 0.7);
28066                             width = parseFloat(s.style('stroke-width') || 10);
28067                         }
28068
28069                         // calculate from/to interpolation params..
28070                         p.tag = tag;
28071                         p.from.opacity = opacity * 0.6;
28072                         p.to.opacity = opacity * 1.25;
28073                         p.from.width = width * 0.7;
28074                         p.to.width = width * (tag === 'circle' ? 1.5 : 1);
28075                         _params[d.id] = p;
28076                     });
28077             }
28078
28079
28080             function run(surface, fromTo) {
28081                 var toFrom = (fromTo === 'from' ? 'to' : 'from');
28082                 var currSelected = surface.selectAll(selector);
28083                 var currClassed = surface.attr('class');
28084
28085                 if (_done || currSelected.empty()) {
28086                     _selected.call(reset);
28087                     _selected = select(null);
28088                     return;
28089                 }
28090
28091                 if (!fastDeepEqual(currSelected.data(), _selected.data()) || currClassed !== _classed) {
28092                     _selected.call(reset);
28093                     _classed = currClassed;
28094                     _selected = currSelected.call(calcAnimationParams);
28095                 }
28096
28097                 var didCallNextRun = false;
28098
28099                 _selected
28100                     .transition()
28101                     .duration(duration)
28102                     .call(setAnimationParams, fromTo)
28103                     .on('end', function() {
28104                         // `end` event is called for each selected element, but we want
28105                         // it to run only once
28106                         if (!didCallNextRun) {
28107                             surface.call(run, toFrom);
28108                             didCallNextRun = true;
28109                         }
28110
28111                         // if entity was deselected, remove breathe styling
28112                         if (!select(this).classed('selected')) {
28113                             reset(select(this));
28114                         }
28115                     });
28116             }
28117
28118             function behavior(surface) {
28119                 _done = false;
28120                 _timer = timer(function() {
28121                     // wait for elements to actually become selected
28122                     if (surface.selectAll(selector).empty()) {
28123                         return false;
28124                     }
28125
28126                     surface.call(run, 'from');
28127                     _timer.stop();
28128                     return true;
28129                 }, 20);
28130             }
28131
28132             behavior.restartIfNeeded = function(surface) {
28133                 if (_selected.empty()) {
28134                     surface.call(run, 'from');
28135                     if (_timer) {
28136                         _timer.stop();
28137                     }
28138                 }
28139             };
28140
28141             behavior.off = function() {
28142                 _done = true;
28143                 if (_timer) {
28144                     _timer.stop();
28145                 }
28146                 _selected
28147                     .interrupt()
28148                     .call(reset);
28149             };
28150
28151
28152             return behavior;
28153         }
28154
28155         /* Creates a keybinding behavior for an operation */
28156         function behaviorOperation(context) {
28157             var _operation;
28158
28159             function keypress() {
28160                 // prevent operations during low zoom selection
28161                 if (!context.map().withinEditableZoom()) return;
28162
28163                 event.preventDefault();
28164                 var disabled = _operation.disabled();
28165
28166                 if (disabled) {
28167                     context.ui().flash
28168                         .duration(4000)
28169                         .iconName('#iD-operation-' + _operation.id)
28170                         .iconClass('operation disabled')
28171                         .text(_operation.tooltip)();
28172
28173                 } else {
28174                     context.ui().flash
28175                         .duration(2000)
28176                         .iconName('#iD-operation-' + _operation.id)
28177                         .iconClass('operation')
28178                         .text(_operation.annotation() || _operation.title)();
28179
28180                     if (_operation.point) _operation.point(null);
28181                     _operation();
28182                 }
28183             }
28184
28185
28186             function behavior() {
28187                 if (_operation && _operation.available()) {
28188                     context.keybinding()
28189                         .on(_operation.keys, keypress);
28190                 }
28191
28192                 return behavior;
28193             }
28194
28195
28196             behavior.off = function() {
28197                 context.keybinding()
28198                     .off(_operation.keys);
28199             };
28200
28201
28202             behavior.which = function (_) {
28203                 if (!arguments.length) return _operation;
28204                 _operation = _;
28205                 return behavior;
28206             };
28207
28208
28209             return behavior;
28210         }
28211
28212         function operationCircularize(context, selectedIDs) {
28213             var _extent;
28214             var _actions = selectedIDs.map(getAction).filter(Boolean);
28215             var _amount = _actions.length === 1 ? 'single' : 'multiple';
28216             var _coords = utilGetAllNodes(selectedIDs, context.graph())
28217                 .map(function(n) { return n.loc; });
28218
28219             function getAction(entityID) {
28220
28221                 var entity = context.entity(entityID);
28222
28223                 if (entity.type !== 'way' || new Set(entity.nodes).size <= 1) return null;
28224
28225                 if (!_extent) {
28226                     _extent =  entity.extent(context.graph());
28227                 } else {
28228                     _extent = _extent.extend(entity.extent(context.graph()));
28229                 }
28230
28231                 return actionCircularize(entityID, context.projection);
28232             }
28233
28234             var operation = function() {
28235                 if (!_actions.length) return;
28236
28237                 var combinedAction = function(graph, t) {
28238                     _actions.forEach(function(action) {
28239                         if (!action.disabled(graph)) {
28240                             graph = action(graph, t);
28241                         }
28242                     });
28243                     return graph;
28244                 };
28245                 combinedAction.transitionable = true;
28246
28247                 context.perform(combinedAction, operation.annotation());
28248
28249                 window.setTimeout(function() {
28250                     context.validator().validate();
28251                 }, 300);  // after any transition
28252             };
28253
28254
28255             operation.available = function() {
28256                 return _actions.length && selectedIDs.length === _actions.length;
28257             };
28258
28259
28260             // don't cache this because the visible extent could change
28261             operation.disabled = function() {
28262                 if (!_actions.length) return '';
28263
28264                 var actionDisableds = _actions.map(function(action) {
28265                     return action.disabled(context.graph());
28266                 }).filter(Boolean);
28267
28268                 if (actionDisableds.length === _actions.length) {
28269                     // none of the features can be circularized
28270
28271                     if (new Set(actionDisableds).size > 1) {
28272                         return 'multiple_blockers';
28273                     }
28274                     return actionDisableds[0];
28275                 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
28276                     return 'too_large';
28277                 } else if (someMissing()) {
28278                     return 'not_downloaded';
28279                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28280                     return 'connected_to_hidden';
28281                 }
28282
28283                 return false;
28284
28285
28286                 function someMissing() {
28287                     if (context.inIntro()) return false;
28288                     var osm = context.connection();
28289                     if (osm) {
28290                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28291                         if (missing.length) {
28292                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28293                             return true;
28294                         }
28295                     }
28296                     return false;
28297                 }
28298             };
28299
28300
28301             operation.tooltip = function() {
28302                 var disable = operation.disabled();
28303                 return disable ?
28304                     _t('operations.circularize.' + disable + '.' + _amount) :
28305                     _t('operations.circularize.description.' + _amount);
28306             };
28307
28308
28309             operation.annotation = function() {
28310                 return _t('operations.circularize.annotation.' + _amount);
28311             };
28312
28313
28314             operation.id = 'circularize';
28315             operation.keys = [_t('operations.circularize.key')];
28316             operation.title = _t('operations.circularize.title');
28317             operation.behavior = behaviorOperation(context).which(operation);
28318
28319             return operation;
28320         }
28321
28322         // Translate a MacOS key command into the appropriate Windows/Linux equivalent.
28323         // For example, ⌘Z -> Ctrl+Z
28324         var uiCmd = function (code) {
28325             var detected = utilDetect();
28326
28327             if (detected.os === 'mac') {
28328                 return code;
28329             }
28330
28331             if (detected.os === 'win') {
28332                 if (code === '⌘⇧Z') return 'Ctrl+Y';
28333             }
28334
28335             var result = '',
28336                 replacements = {
28337                     '⌘': 'Ctrl',
28338                     '⇧': 'Shift',
28339                     '⌥': 'Alt',
28340                     '⌫': 'Backspace',
28341                     '⌦': 'Delete'
28342                 };
28343
28344             for (var i = 0; i < code.length; i++) {
28345                 if (code[i] in replacements) {
28346                     result += replacements[code[i]] + (i < code.length - 1 ? '+' : '');
28347                 } else {
28348                     result += code[i];
28349                 }
28350             }
28351
28352             return result;
28353         };
28354
28355
28356         // return a display-focused string for a given keyboard code
28357         uiCmd.display = function(code) {
28358             if (code.length !== 1) return code;
28359
28360             var detected = utilDetect();
28361             var mac = (detected.os === 'mac');
28362             var replacements = {
28363                 '⌘': mac ? '⌘ ' + _t('shortcuts.key.cmd')    : _t('shortcuts.key.ctrl'),
28364                 '⇧': mac ? '⇧ ' + _t('shortcuts.key.shift')  : _t('shortcuts.key.shift'),
28365                 '⌥': mac ? '⌥ ' + _t('shortcuts.key.option') : _t('shortcuts.key.alt'),
28366                 '⌃': mac ? '⌃ ' + _t('shortcuts.key.ctrl')   : _t('shortcuts.key.ctrl'),
28367                 '⌫': mac ? '⌫ ' + _t('shortcuts.key.delete') : _t('shortcuts.key.backspace'),
28368                 '⌦': mac ? '⌦ ' + _t('shortcuts.key.del')    : _t('shortcuts.key.del'),
28369                 '↖': mac ? '↖ ' + _t('shortcuts.key.pgup')   : _t('shortcuts.key.pgup'),
28370                 '↘': mac ? '↘ ' + _t('shortcuts.key.pgdn')   : _t('shortcuts.key.pgdn'),
28371                 '⇞': mac ? '⇞ ' + _t('shortcuts.key.home')   : _t('shortcuts.key.home'),
28372                 '⇟': mac ? '⇟ ' + _t('shortcuts.key.end')    : _t('shortcuts.key.end'),
28373                 '↵': mac ? '⏎ ' + _t('shortcuts.key.return') : _t('shortcuts.key.enter'),
28374                 '⎋': mac ? '⎋ ' + _t('shortcuts.key.esc')    : _t('shortcuts.key.esc'),
28375                 '☰': mac ? '☰ ' + _t('shortcuts.key.menu')  : _t('shortcuts.key.menu'),
28376             };
28377
28378             return replacements[code] || code;
28379         };
28380
28381         function operationDelete(context, selectedIDs) {
28382             var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
28383             var action = actionDeleteMultiple(selectedIDs);
28384             var nodes = utilGetAllNodes(selectedIDs, context.graph());
28385             var coords = nodes.map(function(n) { return n.loc; });
28386             var extent = utilTotalExtent(selectedIDs, context.graph());
28387
28388
28389             var operation = function() {
28390                 var nextSelectedID;
28391                 var nextSelectedLoc;
28392
28393                 if (selectedIDs.length === 1) {
28394                     var id = selectedIDs[0];
28395                     var entity = context.entity(id);
28396                     var geometry = entity.geometry(context.graph());
28397                     var parents = context.graph().parentWays(entity);
28398                     var parent = parents[0];
28399
28400                     // Select the next closest node in the way.
28401                     if (geometry === 'vertex') {
28402                         var nodes = parent.nodes;
28403                         var i = nodes.indexOf(id);
28404
28405                         if (i === 0) {
28406                             i++;
28407                         } else if (i === nodes.length - 1) {
28408                             i--;
28409                         } else {
28410                             var a = geoSphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc);
28411                             var b = geoSphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
28412                             i = a < b ? i - 1 : i + 1;
28413                         }
28414
28415                         nextSelectedID = nodes[i];
28416                         nextSelectedLoc = context.entity(nextSelectedID).loc;
28417                     }
28418                 }
28419
28420                 context.perform(action, operation.annotation());
28421                 context.validator().validate();
28422
28423                 if (nextSelectedID && nextSelectedLoc) {
28424                     if (context.hasEntity(nextSelectedID)) {
28425                         context.enter(modeSelect(context, [nextSelectedID]).follow(true));
28426                     } else {
28427                         context.map().centerEase(nextSelectedLoc);
28428                         context.enter(modeBrowse(context));
28429                     }
28430                 } else {
28431                     context.enter(modeBrowse(context));
28432                 }
28433
28434             };
28435
28436
28437             operation.available = function() {
28438                 return true;
28439             };
28440
28441
28442             operation.disabled = function() {
28443                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
28444                     return 'too_large';
28445                 } else if (someMissing()) {
28446                     return 'not_downloaded';
28447                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28448                     return 'connected_to_hidden';
28449                 } else if (selectedIDs.some(protectedMember)) {
28450                     return 'part_of_relation';
28451                 } else if (selectedIDs.some(incompleteRelation)) {
28452                     return 'incomplete_relation';
28453                 } else if (selectedIDs.some(hasWikidataTag)) {
28454                     return 'has_wikidata_tag';
28455                 }
28456
28457                 return false;
28458
28459
28460                 function someMissing() {
28461                     if (context.inIntro()) return false;
28462                     var osm = context.connection();
28463                     if (osm) {
28464                         var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28465                         if (missing.length) {
28466                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28467                             return true;
28468                         }
28469                     }
28470                     return false;
28471                 }
28472
28473                 function hasWikidataTag(id) {
28474                     var entity = context.entity(id);
28475                     return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
28476                 }
28477
28478                 function incompleteRelation(id) {
28479                     var entity = context.entity(id);
28480                     return entity.type === 'relation' && !entity.isComplete(context.graph());
28481                 }
28482
28483                 function protectedMember(id) {
28484                     var entity = context.entity(id);
28485                     if (entity.type !== 'way') return false;
28486
28487                     var parents = context.graph().parentRelations(entity);
28488                     for (var i = 0; i < parents.length; i++) {
28489                         var parent = parents[i];
28490                         var type = parent.tags.type;
28491                         var role = parent.memberById(id).role || 'outer';
28492                         if (type === 'route' || type === 'boundary' || (type === 'multipolygon' && role === 'outer')) {
28493                             return true;
28494                         }
28495                     }
28496                     return false;
28497                 }
28498             };
28499
28500
28501             operation.tooltip = function() {
28502                 var disable = operation.disabled();
28503                 return disable ?
28504                     _t('operations.delete.' + disable + '.' + multi) :
28505                     _t('operations.delete.description' + '.' + multi);
28506             };
28507
28508
28509             operation.annotation = function() {
28510                 return selectedIDs.length === 1 ?
28511                     _t('operations.delete.annotation.' + context.graph().geometry(selectedIDs[0])) :
28512                     _t('operations.delete.annotation.multiple', { n: selectedIDs.length });
28513             };
28514
28515
28516             operation.id = 'delete';
28517             operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
28518             operation.title = _t('operations.delete.title');
28519             operation.behavior = behaviorOperation(context).which(operation);
28520
28521             return operation;
28522         }
28523
28524         function operationOrthogonalize(context, selectedIDs) {
28525             var _extent;
28526             var _type;
28527             var _actions = selectedIDs.map(chooseAction).filter(Boolean);
28528             var _amount = _actions.length === 1 ? 'single' : 'multiple';
28529             var _coords = utilGetAllNodes(selectedIDs, context.graph())
28530                 .map(function(n) { return n.loc; });
28531
28532
28533             function chooseAction(entityID) {
28534
28535                 var entity = context.entity(entityID);
28536                 var geometry = entity.geometry(context.graph());
28537
28538                 if (!_extent) {
28539                     _extent =  entity.extent(context.graph());
28540                 } else {
28541                     _extent = _extent.extend(entity.extent(context.graph()));
28542                 }
28543
28544                 // square a line/area
28545                 if (entity.type === 'way' && new Set(entity.nodes).size > 2 ) {
28546                     if (_type && _type !== 'feature') return null;
28547                     _type = 'feature';
28548                     return actionOrthogonalize(entityID, context.projection);
28549
28550                 // square a single vertex
28551                 } else if (geometry === 'vertex') {
28552                     if (_type && _type !== 'corner') return null;
28553                     _type = 'corner';
28554                     var graph = context.graph();
28555                     var parents = graph.parentWays(entity);
28556                     if (parents.length === 1) {
28557                         var way = parents[0];
28558                         if (way.nodes.indexOf(entityID) !== -1) {
28559                             return actionOrthogonalize(way.id, context.projection, entityID);
28560                         }
28561                     }
28562                 }
28563
28564                 return null;
28565             }
28566
28567
28568             var operation = function() {
28569                 if (!_actions.length) return;
28570
28571                 var combinedAction = function(graph, t) {
28572                     _actions.forEach(function(action) {
28573                         if (!action.disabled(graph)) {
28574                             graph = action(graph, t);
28575                         }
28576                     });
28577                     return graph;
28578                 };
28579                 combinedAction.transitionable = true;
28580
28581                 context.perform(combinedAction, operation.annotation());
28582
28583                 window.setTimeout(function() {
28584                     context.validator().validate();
28585                 }, 300);  // after any transition
28586             };
28587
28588
28589             operation.available = function() {
28590                 return _actions.length && selectedIDs.length === _actions.length;
28591             };
28592
28593
28594             // don't cache this because the visible extent could change
28595             operation.disabled = function() {
28596                 if (!_actions.length) return '';
28597
28598                 var actionDisableds = _actions.map(function(action) {
28599                     return action.disabled(context.graph());
28600                 }).filter(Boolean);
28601
28602                 if (actionDisableds.length === _actions.length) {
28603                     // none of the features can be squared
28604
28605                     if (new Set(actionDisableds).size > 1) {
28606                         return 'multiple_blockers';
28607                     }
28608                     return actionDisableds[0];
28609                 } else if (_extent &&
28610                            _extent.percentContainedIn(context.map().extent()) < 0.8) {
28611                     return 'too_large';
28612                 } else if (someMissing()) {
28613                     return 'not_downloaded';
28614                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28615                     return 'connected_to_hidden';
28616                 }
28617
28618                 return false;
28619
28620
28621                 function someMissing() {
28622                     if (context.inIntro()) return false;
28623                     var osm = context.connection();
28624                     if (osm) {
28625                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28626                         if (missing.length) {
28627                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28628                             return true;
28629                         }
28630                     }
28631                     return false;
28632                 }
28633             };
28634
28635
28636             operation.tooltip = function() {
28637                 var disable = operation.disabled();
28638                 return disable ?
28639                     _t('operations.orthogonalize.' + disable + '.' + _amount) :
28640                     _t('operations.orthogonalize.description.' + _type + '.' + _amount);
28641             };
28642
28643
28644             operation.annotation = function() {
28645                 return _t('operations.orthogonalize.annotation.' + _type + '.' + _amount);
28646             };
28647
28648
28649             operation.id = 'orthogonalize';
28650             operation.keys = [_t('operations.orthogonalize.key')];
28651             operation.title = _t('operations.orthogonalize.title');
28652             operation.behavior = behaviorOperation(context).which(operation);
28653
28654             return operation;
28655         }
28656
28657         function operationReflectShort(context, selectedIDs) {
28658             return operationReflect(context, selectedIDs, 'short');
28659         }
28660
28661
28662         function operationReflectLong(context, selectedIDs) {
28663             return operationReflect(context, selectedIDs, 'long');
28664         }
28665
28666
28667         function operationReflect(context, selectedIDs, axis) {
28668             axis = axis || 'long';
28669             var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
28670             var nodes = utilGetAllNodes(selectedIDs, context.graph());
28671             var coords = nodes.map(function(n) { return n.loc; });
28672             var extent = utilTotalExtent(selectedIDs, context.graph());
28673
28674
28675             var operation = function() {
28676                 var action = actionReflect(selectedIDs, context.projection)
28677                     .useLongAxis(Boolean(axis === 'long'));
28678
28679                 context.perform(action, operation.annotation());
28680
28681                 window.setTimeout(function() {
28682                     context.validator().validate();
28683                 }, 300);  // after any transition
28684             };
28685
28686
28687             operation.available = function() {
28688                 return nodes.length >= 3;
28689             };
28690
28691
28692             // don't cache this because the visible extent could change
28693             operation.disabled = function() {
28694                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
28695                     return 'too_large';
28696                 } else if (someMissing()) {
28697                     return 'not_downloaded';
28698                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28699                     return 'connected_to_hidden';
28700                 } else if (selectedIDs.some(incompleteRelation)) {
28701                     return 'incomplete_relation';
28702                 }
28703
28704                 return false;
28705
28706
28707                 function someMissing() {
28708                     if (context.inIntro()) return false;
28709                     var osm = context.connection();
28710                     if (osm) {
28711                         var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28712                         if (missing.length) {
28713                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28714                             return true;
28715                         }
28716                     }
28717                     return false;
28718                 }
28719
28720                 function incompleteRelation(id) {
28721                     var entity = context.entity(id);
28722                     return entity.type === 'relation' && !entity.isComplete(context.graph());
28723                 }
28724             };
28725
28726
28727             operation.tooltip = function() {
28728                 var disable = operation.disabled();
28729                 return disable ?
28730                     _t('operations.reflect.' + disable + '.' + multi) :
28731                     _t('operations.reflect.description.' + axis + '.' + multi);
28732             };
28733
28734
28735             operation.annotation = function() {
28736                 return _t('operations.reflect.annotation.' + axis + '.' + multi);
28737             };
28738
28739
28740             operation.id = 'reflect-' + axis;
28741             operation.keys = [_t('operations.reflect.key.' + axis)];
28742             operation.title = _t('operations.reflect.title.' + axis);
28743             operation.behavior = behaviorOperation(context).which(operation);
28744
28745             return operation;
28746         }
28747
28748         function operationMove(context, selectedIDs) {
28749             var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
28750             var nodes = utilGetAllNodes(selectedIDs, context.graph());
28751             var coords = nodes.map(function(n) { return n.loc; });
28752             var extent = utilTotalExtent(selectedIDs, context.graph());
28753
28754
28755             var operation = function() {
28756                 context.enter(modeMove(context, selectedIDs));
28757             };
28758
28759
28760             operation.available = function() {
28761                 return selectedIDs.length > 1 ||
28762                     context.entity(selectedIDs[0]).type !== 'node';
28763             };
28764
28765
28766             operation.disabled = function() {
28767                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
28768                     return 'too_large';
28769                 } else if (someMissing()) {
28770                     return 'not_downloaded';
28771                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28772                     return 'connected_to_hidden';
28773                 } else if (selectedIDs.some(incompleteRelation)) {
28774                     return 'incomplete_relation';
28775                 }
28776
28777                 return false;
28778
28779
28780                 function someMissing() {
28781                     if (context.inIntro()) return false;
28782                     var osm = context.connection();
28783                     if (osm) {
28784                         var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28785                         if (missing.length) {
28786                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28787                             return true;
28788                         }
28789                     }
28790                     return false;
28791                 }
28792
28793                 function incompleteRelation(id) {
28794                     var entity = context.entity(id);
28795                     return entity.type === 'relation' && !entity.isComplete(context.graph());
28796                 }
28797             };
28798
28799
28800             operation.tooltip = function() {
28801                 var disable = operation.disabled();
28802                 return disable ?
28803                     _t('operations.move.' + disable + '.' + multi) :
28804                     _t('operations.move.description.' + multi);
28805             };
28806
28807
28808             operation.annotation = function() {
28809                 return selectedIDs.length === 1 ?
28810                     _t('operations.move.annotation.' + context.graph().geometry(selectedIDs[0])) :
28811                     _t('operations.move.annotation.multiple');
28812             };
28813
28814
28815             operation.id = 'move';
28816             operation.keys = [_t('operations.move.key')];
28817             operation.title = _t('operations.move.title');
28818             operation.behavior = behaviorOperation(context).which(operation);
28819
28820             operation.mouseOnly = true;
28821
28822             return operation;
28823         }
28824
28825         function modeRotate(context, entityIDs) {
28826             var mode = {
28827                 id: 'rotate',
28828                 button: 'browse'
28829             };
28830
28831             var keybinding = utilKeybinding('rotate');
28832             var behaviors = [
28833                 behaviorEdit(context),
28834                 operationCircularize(context, entityIDs).behavior,
28835                 operationDelete(context, entityIDs).behavior,
28836                 operationMove(context, entityIDs).behavior,
28837                 operationOrthogonalize(context, entityIDs).behavior,
28838                 operationReflectLong(context, entityIDs).behavior,
28839                 operationReflectShort(context, entityIDs).behavior
28840             ];
28841             var annotation = entityIDs.length === 1 ?
28842                 _t('operations.rotate.annotation.' + context.graph().geometry(entityIDs[0])) :
28843                 _t('operations.rotate.annotation.multiple');
28844
28845             var _prevGraph;
28846             var _prevAngle;
28847             var _prevTransform;
28848             var _pivot;
28849
28850
28851             function doRotate() {
28852                 var fn;
28853                 if (context.graph() !== _prevGraph) {
28854                     fn = context.perform;
28855                 } else {
28856                     fn = context.replace;
28857                 }
28858
28859                 // projection changed, recalculate _pivot
28860                 var projection = context.projection;
28861                 var currTransform = projection.transform();
28862                 if (!_prevTransform ||
28863                     currTransform.k !== _prevTransform.k ||
28864                     currTransform.x !== _prevTransform.x ||
28865                     currTransform.y !== _prevTransform.y) {
28866
28867                     var nodes = utilGetAllNodes(entityIDs, context.graph());
28868                     var points = nodes.map(function(n) { return projection(n.loc); });
28869                     _pivot = getPivot(points);
28870                     _prevAngle = undefined;
28871                 }
28872
28873
28874                 var currMouse = context.map().mouse();
28875                 var currAngle = Math.atan2(currMouse[1] - _pivot[1], currMouse[0] - _pivot[0]);
28876
28877                 if (typeof _prevAngle === 'undefined') _prevAngle = currAngle;
28878                 var delta = currAngle - _prevAngle;
28879
28880                 fn(actionRotate(entityIDs, _pivot, delta, projection));
28881
28882                 _prevTransform = currTransform;
28883                 _prevAngle = currAngle;
28884                 _prevGraph = context.graph();
28885             }
28886
28887             function getPivot(points) {
28888                 var _pivot;
28889                 if (points.length === 1) {
28890                     _pivot = points[0];
28891                 } else if (points.length === 2) {
28892                     _pivot = geoVecInterp(points[0], points[1], 0.5);
28893                 } else {
28894                     var polygonHull = d3_polygonHull(points);
28895                     if (polygonHull.length === 2) {
28896                         _pivot = geoVecInterp(points[0], points[1], 0.5);
28897                     } else {
28898                         _pivot = d3_polygonCentroid(d3_polygonHull(points));
28899                     }
28900                 }
28901                 return _pivot;
28902             }
28903
28904
28905             function finish() {
28906                 event.stopPropagation();
28907                 context.replace(actionNoop(), annotation);
28908                 context.enter(modeSelect(context, entityIDs));
28909             }
28910
28911
28912             function cancel() {
28913                 context.pop();
28914                 context.enter(modeSelect(context, entityIDs));
28915             }
28916
28917
28918             function undone() {
28919                 context.enter(modeBrowse(context));
28920             }
28921
28922
28923             mode.enter = function() {
28924                 context.features().forceVisible(entityIDs);
28925
28926                 behaviors.forEach(context.install);
28927
28928                 context.surface()
28929                     .on('mousemove.rotate', doRotate)
28930                     .on('click.rotate', finish);
28931
28932                 context.history()
28933                     .on('undone.rotate', undone);
28934
28935                 keybinding
28936                     .on('⎋', cancel)
28937                     .on('↩', finish);
28938
28939                 select(document)
28940                     .call(keybinding);
28941             };
28942
28943
28944             mode.exit = function() {
28945                 behaviors.forEach(context.uninstall);
28946
28947                 context.surface()
28948                     .on('mousemove.rotate', null)
28949                     .on('click.rotate', null);
28950
28951                 context.history()
28952                     .on('undone.rotate', null);
28953
28954                 select(document)
28955                     .call(keybinding.unbind);
28956
28957                 context.features().forceVisible([]);
28958             };
28959
28960
28961             mode.selectedIDs = function() {
28962                 if (!arguments.length) return entityIDs;
28963                 // no assign
28964                 return mode;
28965             };
28966
28967
28968             return mode;
28969         }
28970
28971         function operationRotate(context, selectedIDs) {
28972             var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
28973             var nodes = utilGetAllNodes(selectedIDs, context.graph());
28974             var coords = nodes.map(function(n) { return n.loc; });
28975             var extent = utilTotalExtent(selectedIDs, context.graph());
28976
28977
28978             var operation = function() {
28979                 context.enter(modeRotate(context, selectedIDs));
28980             };
28981
28982
28983             operation.available = function() {
28984                 return nodes.length >= 2;
28985             };
28986
28987
28988             operation.disabled = function() {
28989
28990                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
28991                     return 'too_large';
28992                 } else if (someMissing()) {
28993                     return 'not_downloaded';
28994                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28995                     return 'connected_to_hidden';
28996                 } else if (selectedIDs.some(incompleteRelation)) {
28997                     return 'incomplete_relation';
28998                 }
28999
29000                 return false;
29001
29002
29003                 function someMissing() {
29004                     if (context.inIntro()) return false;
29005                     var osm = context.connection();
29006                     if (osm) {
29007                         var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
29008                         if (missing.length) {
29009                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
29010                             return true;
29011                         }
29012                     }
29013                     return false;
29014                 }
29015
29016                 function incompleteRelation(id) {
29017                     var entity = context.entity(id);
29018                     return entity.type === 'relation' && !entity.isComplete(context.graph());
29019                 }
29020             };
29021
29022
29023             operation.tooltip = function() {
29024                 var disable = operation.disabled();
29025                 return disable ?
29026                     _t('operations.rotate.' + disable + '.' + multi) :
29027                     _t('operations.rotate.description.' + multi);
29028             };
29029
29030
29031             operation.annotation = function() {
29032                 return selectedIDs.length === 1 ?
29033                     _t('operations.rotate.annotation.' + context.graph().geometry(selectedIDs[0])) :
29034                     _t('operations.rotate.annotation.multiple');
29035             };
29036
29037
29038             operation.id = 'rotate';
29039             operation.keys = [_t('operations.rotate.key')];
29040             operation.title = _t('operations.rotate.title');
29041             operation.behavior = behaviorOperation(context).which(operation);
29042
29043             operation.mouseOnly = true;
29044
29045             return operation;
29046         }
29047
29048         function modeMove(context, entityIDs, baseGraph) {
29049             var mode = {
29050                 id: 'move',
29051                 button: 'browse'
29052             };
29053
29054             var keybinding = utilKeybinding('move');
29055             var behaviors = [
29056                 behaviorEdit(context),
29057                 operationCircularize(context, entityIDs).behavior,
29058                 operationDelete(context, entityIDs).behavior,
29059                 operationOrthogonalize(context, entityIDs).behavior,
29060                 operationReflectLong(context, entityIDs).behavior,
29061                 operationReflectShort(context, entityIDs).behavior,
29062                 operationRotate(context, entityIDs).behavior
29063             ];
29064             var annotation = entityIDs.length === 1 ?
29065                 _t('operations.move.annotation.' + context.graph().geometry(entityIDs[0])) :
29066                 _t('operations.move.annotation.multiple');
29067
29068             var _prevGraph;
29069             var _cache;
29070             var _origin;
29071             var _nudgeInterval;
29072
29073
29074             function doMove(nudge) {
29075                 nudge = nudge || [0, 0];
29076
29077                 var fn;
29078                 if (_prevGraph !== context.graph()) {
29079                     _cache = {};
29080                     _origin = context.map().mouseCoordinates();
29081                     fn = context.perform;
29082                 } else {
29083                     fn = context.overwrite;
29084                 }
29085
29086                 var currMouse = context.map().mouse();
29087                 var origMouse = context.projection(_origin);
29088                 var delta = geoVecSubtract(geoVecSubtract(currMouse, origMouse), nudge);
29089
29090                 fn(actionMove(entityIDs, delta, context.projection, _cache));
29091                 _prevGraph = context.graph();
29092             }
29093
29094
29095             function startNudge(nudge) {
29096                 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
29097                 _nudgeInterval = window.setInterval(function() {
29098                     context.map().pan(nudge);
29099                     doMove(nudge);
29100                 }, 50);
29101             }
29102
29103
29104             function stopNudge() {
29105                 if (_nudgeInterval) {
29106                     window.clearInterval(_nudgeInterval);
29107                     _nudgeInterval = null;
29108                 }
29109             }
29110
29111
29112             function move() {
29113                 doMove();
29114                 var nudge = geoViewportEdge(context.map().mouse(), context.map().dimensions());
29115                 if (nudge) {
29116                     startNudge(nudge);
29117                 } else {
29118                     stopNudge();
29119                 }
29120             }
29121
29122
29123             function finish() {
29124                 event.stopPropagation();
29125                 context.replace(actionNoop(), annotation);
29126                 context.enter(modeSelect(context, entityIDs));
29127                 stopNudge();
29128             }
29129
29130
29131             function cancel() {
29132                 if (baseGraph) {
29133                     while (context.graph() !== baseGraph) context.pop();
29134                     context.enter(modeBrowse(context));
29135                 } else {
29136                     context.pop();
29137                     context.enter(modeSelect(context, entityIDs));
29138                 }
29139                 stopNudge();
29140             }
29141
29142
29143             function undone() {
29144                 context.enter(modeBrowse(context));
29145             }
29146
29147
29148             mode.enter = function() {
29149                 _origin = context.map().mouseCoordinates();
29150                 _prevGraph = null;
29151                 _cache = {};
29152
29153                 context.features().forceVisible(entityIDs);
29154
29155                 behaviors.forEach(context.install);
29156
29157                 context.surface()
29158                     .on('mousemove.move', move)
29159                     .on('click.move', finish);
29160
29161                 context.history()
29162                     .on('undone.move', undone);
29163
29164                 keybinding
29165                     .on('⎋', cancel)
29166                     .on('↩', finish);
29167
29168                 select(document)
29169                     .call(keybinding);
29170             };
29171
29172
29173             mode.exit = function() {
29174                 stopNudge();
29175
29176                 behaviors.forEach(function(behavior) {
29177                     context.uninstall(behavior);
29178                 });
29179
29180                 context.surface()
29181                     .on('mousemove.move', null)
29182                     .on('click.move', null);
29183
29184                 context.history()
29185                     .on('undone.move', null);
29186
29187                 select(document)
29188                     .call(keybinding.unbind);
29189
29190                 context.features().forceVisible([]);
29191             };
29192
29193
29194             mode.selectedIDs = function() {
29195                 if (!arguments.length) return entityIDs;
29196                 // no assign
29197                 return mode;
29198             };
29199
29200
29201             return mode;
29202         }
29203
29204         // see also `operationPaste`
29205         function behaviorPaste(context) {
29206
29207             function doPaste() {
29208                 // prevent paste during low zoom selection
29209                 if (!context.map().withinEditableZoom()) return;
29210
29211                 event.preventDefault();
29212
29213                 var baseGraph = context.graph();
29214                 var mouse = context.map().mouse();
29215                 var projection = context.projection;
29216                 var viewport = geoExtent(projection.clipExtent()).polygon();
29217
29218                 if (!geoPointInPolygon(mouse, viewport)) return;
29219
29220                 var oldIDs = context.copyIDs();
29221                 if (!oldIDs.length) return;
29222
29223                 var extent = geoExtent();
29224                 var oldGraph = context.copyGraph();
29225                 var newIDs = [];
29226
29227                 var action = actionCopyEntities(oldIDs, oldGraph);
29228                 context.perform(action);
29229
29230                 var copies = action.copies();
29231                 var originals = new Set();
29232                 Object.values(copies).forEach(function(entity) { originals.add(entity.id); });
29233
29234                 for (var id in copies) {
29235                     var oldEntity = oldGraph.entity(id);
29236                     var newEntity = copies[id];
29237
29238                     extent._extend(oldEntity.extent(oldGraph));
29239
29240                     // Exclude child nodes from newIDs if their parent way was also copied.
29241                     var parents = context.graph().parentWays(newEntity);
29242                     var parentCopied = parents.some(function(parent) {
29243                         return originals.has(parent.id);
29244                     });
29245
29246                     if (!parentCopied) {
29247                         newIDs.push(newEntity.id);
29248                     }
29249                 }
29250
29251                 // Put pasted objects where mouse pointer is..
29252                 var copyPoint = (context.copyLonLat() && projection(context.copyLonLat())) || projection(extent.center());
29253                 var delta = geoVecSubtract(mouse, copyPoint);
29254
29255                 context.perform(actionMove(newIDs, delta, projection));
29256                 context.enter(modeMove(context, newIDs, baseGraph));
29257             }
29258
29259
29260             function behavior() {
29261                 context.keybinding().on(uiCmd('⌘V'), doPaste);
29262                 return behavior;
29263             }
29264
29265
29266             behavior.off = function() {
29267                 context.keybinding().off(uiCmd('⌘V'));
29268             };
29269
29270
29271             return behavior;
29272         }
29273
29274         /*
29275             `behaviorDrag` is like `d3_behavior.drag`, with the following differences:
29276
29277             * The `origin` function is expected to return an [x, y] tuple rather than an
29278               {x, y} object.
29279             * The events are `start`, `move`, and `end`.
29280               (https://github.com/mbostock/d3/issues/563)
29281             * The `start` event is not dispatched until the first cursor movement occurs.
29282               (https://github.com/mbostock/d3/pull/368)
29283             * The `move` event has a `point` and `delta` [x, y] tuple properties rather
29284               than `x`, `y`, `dx`, and `dy` properties.
29285             * The `end` event is not dispatched if no movement occurs.
29286             * An `off` function is available that unbinds the drag's internal event handlers.
29287          */
29288
29289         function behaviorDrag() {
29290             var dispatch$1 = dispatch('start', 'move', 'end');
29291
29292             // see also behaviorSelect
29293             var _tolerancePx = 1; // keep this low to facilitate pixel-perfect micromapping
29294             var _penTolerancePx = 4; // styluses can be touchy so require greater movement - #1981
29295
29296             var _origin = null;
29297             var _selector = '';
29298             var _event;
29299             var _target;
29300             var _surface;
29301             var _pointerId;
29302
29303             // use pointer events on supported platforms; fallback to mouse events
29304             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
29305
29306             var d3_event_userSelectProperty = utilPrefixCSSProperty('UserSelect');
29307             var d3_event_userSelectSuppress = function() {
29308                     var selection$1 = selection();
29309                     var select = selection$1.style(d3_event_userSelectProperty);
29310                     selection$1.style(d3_event_userSelectProperty, 'none');
29311                     return function() {
29312                         selection$1.style(d3_event_userSelectProperty, select);
29313                     };
29314                 };
29315
29316
29317             function eventOf(thiz, argumentz) {
29318                 return function(e1) {
29319                     e1.target = behavior;
29320                     customEvent(e1, dispatch$1.apply, dispatch$1, [e1.type, thiz, argumentz]);
29321                 };
29322             }
29323
29324
29325             function pointerdown() {
29326
29327                 if (_pointerId) return;
29328
29329                 _pointerId = event.pointerId || 'mouse';
29330
29331                 _target = this;
29332                 _event = eventOf(_target, arguments);
29333
29334                 // only force reflow once per drag
29335                 var pointerLocGetter = utilFastMouse(_surface || _target.parentNode);
29336
29337                 var offset;
29338                 var startOrigin = pointerLocGetter(event);
29339                 var started = false;
29340                 var selectEnable = d3_event_userSelectSuppress();
29341
29342                 select(window)
29343                     .on(_pointerPrefix + 'move.drag', pointermove)
29344                     .on(_pointerPrefix + 'up.drag pointercancel.drag', pointerup, true);
29345
29346                 if (_origin) {
29347                     offset = _origin.apply(_target, arguments);
29348                     offset = [offset[0] - startOrigin[0], offset[1] - startOrigin[1]];
29349                 } else {
29350                     offset = [0, 0];
29351                 }
29352
29353                 event.stopPropagation();
29354
29355
29356                 function pointermove() {
29357                     if (_pointerId !== (event.pointerId || 'mouse')) return;
29358
29359                     var p = pointerLocGetter(event);
29360
29361                     if (!started) {
29362                         var dist = geoVecLength(startOrigin,  p);
29363                         var tolerance = event.pointerType === 'pen' ? _penTolerancePx : _tolerancePx;
29364                         // don't start until the drag has actually moved somewhat
29365                         if (dist < tolerance) return;
29366
29367                         started = true;
29368                         _event({ type: 'start' });
29369
29370                     // Don't send a `move` event in the same cycle as `start` since dragging
29371                     // a midpoint will convert the target to a node.
29372                     } else {
29373
29374                         startOrigin = p;
29375                         event.stopPropagation();
29376                         event.preventDefault();
29377
29378                         var dx = p[0] - startOrigin[0];
29379                         var dy = p[1] - startOrigin[1];
29380                         _event({
29381                             type: 'move',
29382                             point: [p[0] + offset[0],  p[1] + offset[1]],
29383                             delta: [dx, dy]
29384                         });
29385                     }
29386                 }
29387
29388
29389                 function pointerup() {
29390                     if (_pointerId !== (event.pointerId || 'mouse')) return;
29391
29392                     _pointerId = null;
29393
29394                     if (started) {
29395                         _event({ type: 'end' });
29396
29397                         event.preventDefault();
29398                     }
29399
29400                     select(window)
29401                         .on(_pointerPrefix + 'move.drag', null)
29402                         .on(_pointerPrefix + 'up.drag pointercancel.drag', null);
29403
29404                     selectEnable();
29405                 }
29406             }
29407
29408
29409             function behavior(selection) {
29410                 _pointerId = null;
29411                 var matchesSelector = utilPrefixDOMProperty('matchesSelector');
29412                 var delegate = pointerdown;
29413
29414                 if (_selector) {
29415                     delegate = function() {
29416                         var root = this;
29417                         var target = event.target;
29418                         for (; target && target !== root; target = target.parentNode) {
29419                             var datum = target.__data__;
29420
29421                             var entity = datum instanceof osmNote ? datum
29422                                 : datum && datum.properties && datum.properties.entity;
29423
29424                             if (entity && target[matchesSelector](_selector)) {
29425                                 return pointerdown.call(target, entity);
29426                             }
29427                         }
29428                     };
29429                 }
29430
29431                 selection
29432                     .on(_pointerPrefix + 'down.drag' + _selector, delegate);
29433             }
29434
29435
29436             behavior.off = function(selection) {
29437                 selection
29438                     .on(_pointerPrefix + 'down.drag' + _selector, null);
29439             };
29440
29441
29442             behavior.selector = function(_) {
29443                 if (!arguments.length) return _selector;
29444                 _selector = _;
29445                 return behavior;
29446             };
29447
29448
29449             behavior.origin = function(_) {
29450                 if (!arguments.length) return _origin;
29451                 _origin = _;
29452                 return behavior;
29453             };
29454
29455
29456             behavior.cancel = function() {
29457                 select(window)
29458                     .on(_pointerPrefix + 'move.drag', null)
29459                     .on(_pointerPrefix + 'up.drag pointercancel.drag', null);
29460                 return behavior;
29461             };
29462
29463
29464             behavior.target = function() {
29465                 if (!arguments.length) return _target;
29466                 _target = arguments[0];
29467                 _event = eventOf(_target, Array.prototype.slice.call(arguments, 1));
29468                 return behavior;
29469             };
29470
29471
29472             behavior.surface = function() {
29473                 if (!arguments.length) return _surface;
29474                 _surface = arguments[0];
29475                 return behavior;
29476             };
29477
29478
29479             return utilRebind(behavior, dispatch$1, 'on');
29480         }
29481
29482         function modeDragNode(context) {
29483             var mode = {
29484                 id: 'drag-node',
29485                 button: 'browse'
29486             };
29487             var hover = behaviorHover(context).altDisables(true)
29488                 .on('hover', context.ui().sidebar.hover);
29489             var edit = behaviorEdit(context);
29490
29491             var _nudgeInterval;
29492             var _restoreSelectedIDs = [];
29493             var _wasMidpoint = false;
29494             var _isCancelled = false;
29495             var _activeEntity;
29496             var _startLoc;
29497             var _lastLoc;
29498
29499
29500             function startNudge(entity, nudge) {
29501                 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
29502                 _nudgeInterval = window.setInterval(function() {
29503                     context.map().pan(nudge);
29504                     doMove(entity, nudge);
29505                 }, 50);
29506             }
29507
29508
29509             function stopNudge() {
29510                 if (_nudgeInterval) {
29511                     window.clearInterval(_nudgeInterval);
29512                     _nudgeInterval = null;
29513                 }
29514             }
29515
29516
29517             function moveAnnotation(entity) {
29518                 return _t('operations.move.annotation.' + entity.geometry(context.graph()));
29519             }
29520
29521
29522             function connectAnnotation(nodeEntity, targetEntity) {
29523                 var nodeGeometry = nodeEntity.geometry(context.graph());
29524                 var targetGeometry = targetEntity.geometry(context.graph());
29525                 if (nodeGeometry === 'vertex' && targetGeometry === 'vertex') {
29526                     var nodeParentWayIDs = context.graph().parentWays(nodeEntity);
29527                     var targetParentWayIDs = context.graph().parentWays(targetEntity);
29528                     var sharedParentWays = utilArrayIntersection(nodeParentWayIDs, targetParentWayIDs);
29529                     // if both vertices are part of the same way
29530                     if (sharedParentWays.length !== 0) {
29531                         // if the nodes are next to each other, they are merged
29532                         if (sharedParentWays[0].areAdjacent(nodeEntity.id, targetEntity.id)) {
29533                             return _t('operations.connect.annotation.from_vertex.to_adjacent_vertex');
29534                         }
29535                         return _t('operations.connect.annotation.from_vertex.to_sibling_vertex');
29536                     }
29537                 }
29538                 return _t('operations.connect.annotation.from_' + nodeGeometry + '.to_' + targetGeometry);
29539             }
29540
29541
29542             function shouldSnapToNode(target) {
29543                 if (!_activeEntity) return false;
29544                 return _activeEntity.geometry(context.graph()) !== 'vertex' ||
29545                     (target.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(target, context.graph()));
29546             }
29547
29548
29549             function origin(entity) {
29550                 return context.projection(entity.loc);
29551             }
29552
29553
29554             function keydown() {
29555                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
29556                     if (context.surface().classed('nope')) {
29557                         context.surface()
29558                             .classed('nope-suppressed', true);
29559                     }
29560                     context.surface()
29561                         .classed('nope', false)
29562                         .classed('nope-disabled', true);
29563                 }
29564             }
29565
29566
29567             function keyup() {
29568                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
29569                     if (context.surface().classed('nope-suppressed')) {
29570                         context.surface()
29571                             .classed('nope', true);
29572                     }
29573                     context.surface()
29574                         .classed('nope-suppressed', false)
29575                         .classed('nope-disabled', false);
29576                 }
29577             }
29578
29579
29580             function start(entity) {
29581                 _wasMidpoint = entity.type === 'midpoint';
29582                 var hasHidden = context.features().hasHiddenConnections(entity, context.graph());
29583                 _isCancelled = !context.editable() || event.sourceEvent.shiftKey || hasHidden;
29584
29585
29586                 if (_isCancelled) {
29587                     if (hasHidden) {
29588                         context.ui().flash
29589                             .duration(4000)
29590                             .text(_t('modes.drag_node.connected_to_hidden'))();
29591                     }
29592                     return drag.cancel();
29593                 }
29594
29595                 if (_wasMidpoint) {
29596                     var midpoint = entity;
29597                     entity = osmNode();
29598                     context.perform(actionAddMidpoint(midpoint, entity));
29599                     entity = context.entity(entity.id);   // get post-action entity
29600
29601                     var vertex = context.surface().selectAll('.' + entity.id);
29602                     drag.target(vertex.node(), entity);
29603
29604                 } else {
29605                     context.perform(actionNoop());
29606                 }
29607
29608                 _activeEntity = entity;
29609                 _startLoc = entity.loc;
29610
29611                 hover.ignoreVertex(entity.geometry(context.graph()) === 'vertex');
29612
29613                 context.surface().selectAll('.' + _activeEntity.id)
29614                     .classed('active', true);
29615
29616                 context.enter(mode);
29617             }
29618
29619
29620             // related code
29621             // - `behavior/draw.js` `datum()`
29622             function datum() {
29623                 var event$1 = event && event.sourceEvent;
29624                 if (!event$1 || event$1.altKey) {
29625                     return {};
29626                 } else {
29627                     // When dragging, snap only to touch targets..
29628                     // (this excludes area fills and active drawing elements)
29629                     var d = event$1.target.__data__;
29630                     return (d && d.properties && d.properties.target) ? d : {};
29631                 }
29632             }
29633
29634
29635             function doMove(entity, nudge) {
29636                 nudge = nudge || [0, 0];
29637
29638                 var currPoint = (event && event.point) || context.projection(_lastLoc);
29639                 var currMouse = geoVecSubtract(currPoint, nudge);
29640                 var loc = context.projection.invert(currMouse);
29641
29642                 if (!_nudgeInterval) {   // If not nudging at the edge of the viewport, try to snap..
29643                     // related code
29644                     // - `mode/drag_node.js`     `doMove()`
29645                     // - `behavior/draw.js`      `click()`
29646                     // - `behavior/draw_way.js`  `move()`
29647                     var d = datum();
29648                     var target = d && d.properties && d.properties.entity;
29649                     var targetLoc = target && target.loc;
29650                     var targetNodes = d && d.properties && d.properties.nodes;
29651                     var edge;
29652
29653                     if (targetLoc) {   // snap to node/vertex - a point target with `.loc`
29654                         if (shouldSnapToNode(target)) {
29655                             loc = targetLoc;
29656                         }
29657
29658                     } else if (targetNodes) {   // snap to way - a line target with `.nodes`
29659                         edge = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, end.id);
29660                         if (edge) {
29661                             loc = edge.loc;
29662                         }
29663                     }
29664                 }
29665
29666                 context.replace(
29667                     actionMoveNode(entity.id, loc)
29668                 );
29669
29670                 // Below here: validations
29671                 var isInvalid = false;
29672
29673                 // Check if this connection to `target` could cause relations to break..
29674                 if (target) {
29675                     isInvalid = hasRelationConflict(entity, target, edge, context.graph());
29676                 }
29677
29678                 // Check if this drag causes the geometry to break..
29679                 if (!isInvalid) {
29680                     isInvalid = hasInvalidGeometry(entity, context.graph());
29681                 }
29682
29683
29684                 var nope = context.surface().classed('nope');
29685                 if (isInvalid === 'relation' || isInvalid === 'restriction') {
29686                     if (!nope) {   // about to nope - show hint
29687                         context.ui().flash
29688                             .duration(4000)
29689                             .text(_t('operations.connect.' + isInvalid,
29690                                 { relation: _mainPresetIndex.item('type/restriction').name() }
29691                             ))();
29692                     }
29693                 } else if (isInvalid) {
29694                     var errorID = isInvalid === 'line' ? 'lines' : 'areas';
29695                     context.ui().flash
29696                         .duration(3000)
29697                         .text(_t('self_intersection.error.' + errorID))();
29698                 } else {
29699                     if (nope) {   // about to un-nope, remove hint
29700                         context.ui().flash
29701                             .duration(1)
29702                             .text('')();
29703                     }
29704                 }
29705
29706
29707                 var nopeDisabled = context.surface().classed('nope-disabled');
29708                 if (nopeDisabled) {
29709                     context.surface()
29710                         .classed('nope', false)
29711                         .classed('nope-suppressed', isInvalid);
29712                 } else {
29713                     context.surface()
29714                         .classed('nope', isInvalid)
29715                         .classed('nope-suppressed', false);
29716                 }
29717
29718                 _lastLoc = loc;
29719             }
29720
29721
29722             // Uses `actionConnect.disabled()` to know whether this connection is ok..
29723             function hasRelationConflict(entity, target, edge, graph) {
29724                 var testGraph = graph.update();  // copy
29725
29726                 // if snapping to way - add midpoint there and consider that the target..
29727                 if (edge) {
29728                     var midpoint = osmNode();
29729                     var action = actionAddMidpoint({
29730                         loc: edge.loc,
29731                         edge: [target.nodes[edge.index - 1], target.nodes[edge.index]]
29732                     }, midpoint);
29733
29734                     testGraph = action(testGraph);
29735                     target = midpoint;
29736                 }
29737
29738                 // can we connect to it?
29739                 var ids = [entity.id, target.id];
29740                 return actionConnect(ids).disabled(testGraph);
29741             }
29742
29743
29744             function hasInvalidGeometry(entity, graph) {
29745                 var parents = graph.parentWays(entity);
29746                 var i, j, k;
29747
29748                 for (i = 0; i < parents.length; i++) {
29749                     var parent = parents[i];
29750                     var nodes = [];
29751                     var activeIndex = null;    // which multipolygon ring contains node being dragged
29752
29753                     // test any parent multipolygons for valid geometry
29754                     var relations = graph.parentRelations(parent);
29755                     for (j = 0; j < relations.length; j++) {
29756                         if (!relations[j].isMultipolygon()) continue;
29757
29758                         var rings = osmJoinWays(relations[j].members, graph);
29759
29760                         // find active ring and test it for self intersections
29761                         for (k = 0; k < rings.length; k++) {
29762                             nodes = rings[k].nodes;
29763                             if (nodes.find(function(n) { return n.id === entity.id; })) {
29764                                 activeIndex = k;
29765                                 if (geoHasSelfIntersections(nodes, entity.id)) {
29766                                     return 'multipolygonMember';
29767                                 }
29768                             }
29769                             rings[k].coords = nodes.map(function(n) { return n.loc; });
29770                         }
29771
29772                         // test active ring for intersections with other rings in the multipolygon
29773                         for (k = 0; k < rings.length; k++) {
29774                             if (k === activeIndex) continue;
29775
29776                             // make sure active ring doesnt cross passive rings
29777                             if (geoHasLineIntersections(rings[activeIndex].nodes, rings[k].nodes, entity.id)) {
29778                                 return 'multipolygonRing';
29779                             }
29780                         }
29781                     }
29782
29783
29784                     // If we still haven't tested this node's parent way for self-intersections.
29785                     // (because it's not a member of a multipolygon), test it now.
29786                     if (activeIndex === null) {
29787                         nodes = parent.nodes.map(function(nodeID) { return graph.entity(nodeID); });
29788                         if (nodes.length && geoHasSelfIntersections(nodes, entity.id)) {
29789                             return parent.geometry(graph);
29790                         }
29791                     }
29792
29793                 }
29794
29795                 return false;
29796             }
29797
29798
29799             function move(entity) {
29800                 if (_isCancelled) return;
29801                 event.sourceEvent.stopPropagation();
29802
29803                 context.surface().classed('nope-disabled', event.sourceEvent.altKey);
29804
29805                 _lastLoc = context.projection.invert(event.point);
29806
29807                 doMove(entity);
29808                 var nudge = geoViewportEdge(event.point, context.map().dimensions());
29809                 if (nudge) {
29810                     startNudge(entity, nudge);
29811                 } else {
29812                     stopNudge();
29813                 }
29814             }
29815
29816             function end(entity) {
29817                 if (_isCancelled) return;
29818
29819                 var wasPoint = entity.geometry(context.graph()) === 'point';
29820
29821                 var d = datum();
29822                 var nope = (d && d.properties && d.properties.nope) || context.surface().classed('nope');
29823                 var target = d && d.properties && d.properties.entity;   // entity to snap to
29824
29825                 if (nope) {   // bounce back
29826                     context.perform(
29827                         _actionBounceBack(entity.id, _startLoc)
29828                     );
29829
29830                 } else if (target && target.type === 'way') {
29831                     var choice = geoChooseEdge(context.graph().childNodes(target), context.map().mouse(), context.projection, entity.id);
29832                     context.replace(
29833                         actionAddMidpoint({
29834                             loc: choice.loc,
29835                             edge: [target.nodes[choice.index - 1], target.nodes[choice.index]]
29836                         }, entity),
29837                         connectAnnotation(entity, target)
29838                     );
29839
29840                 } else if (target && target.type === 'node' && shouldSnapToNode(target)) {
29841                     context.replace(
29842                         actionConnect([target.id, entity.id]),
29843                         connectAnnotation(entity, target)
29844                     );
29845
29846                 } else if (_wasMidpoint) {
29847                     context.replace(
29848                         actionNoop(),
29849                         _t('operations.add.annotation.vertex')
29850                     );
29851
29852                 } else {
29853                     context.replace(
29854                         actionNoop(),
29855                         moveAnnotation(entity)
29856                     );
29857                 }
29858
29859                 if (wasPoint) {
29860                     context.enter(modeSelect(context, [entity.id]));
29861
29862                 } else {
29863                     var reselection = _restoreSelectedIDs.filter(function(id) {
29864                         return context.graph().hasEntity(id);
29865                     });
29866
29867                     if (reselection.length) {
29868                         context.enter(modeSelect(context, reselection));
29869                     } else {
29870                         context.enter(modeBrowse(context));
29871                     }
29872                 }
29873             }
29874
29875
29876             function _actionBounceBack(nodeID, toLoc) {
29877                 var moveNode = actionMoveNode(nodeID, toLoc);
29878                 var action = function(graph, t) {
29879                     // last time through, pop off the bounceback perform.
29880                     // it will then overwrite the initial perform with a moveNode that does nothing
29881                     if (t === 1) context.pop();
29882                     return moveNode(graph, t);
29883                 };
29884                 action.transitionable = true;
29885                 return action;
29886             }
29887
29888
29889             function cancel() {
29890                 drag.cancel();
29891                 context.enter(modeBrowse(context));
29892             }
29893
29894
29895             var drag = behaviorDrag()
29896                 .selector('.layer-touch.points .target')
29897                 .surface(context.container().select('.main-map').node())
29898                 .origin(origin)
29899                 .on('start', start)
29900                 .on('move', move)
29901                 .on('end', end);
29902
29903
29904             mode.enter = function() {
29905                 context.install(hover);
29906                 context.install(edit);
29907
29908                 select(window)
29909                     .on('keydown.dragNode', keydown)
29910                     .on('keyup.dragNode', keyup);
29911
29912                 context.history()
29913                     .on('undone.drag-node', cancel);
29914             };
29915
29916
29917             mode.exit = function() {
29918                 context.ui().sidebar.hover.cancel();
29919                 context.uninstall(hover);
29920                 context.uninstall(edit);
29921
29922                 select(window)
29923                     .on('keydown.dragNode', null)
29924                     .on('keyup.dragNode', null);
29925
29926                 context.history()
29927                     .on('undone.drag-node', null);
29928
29929                 _activeEntity = null;
29930
29931                 context.surface()
29932                     .classed('nope', false)
29933                     .classed('nope-suppressed', false)
29934                     .classed('nope-disabled', false)
29935                     .selectAll('.active')
29936                     .classed('active', false);
29937
29938                 stopNudge();
29939             };
29940
29941
29942             mode.selectedIDs = function() {
29943                 if (!arguments.length) return _activeEntity ? [_activeEntity.id] : [];
29944                 // no assign
29945                 return mode;
29946             };
29947
29948
29949             mode.activeID = function() {
29950                 if (!arguments.length) return _activeEntity && _activeEntity.id;
29951                 // no assign
29952                 return mode;
29953             };
29954
29955
29956             mode.restoreSelectedIDs = function(_) {
29957                 if (!arguments.length) return _restoreSelectedIDs;
29958                 _restoreSelectedIDs = _;
29959                 return mode;
29960             };
29961
29962
29963             mode.behavior = drag;
29964
29965
29966             return mode;
29967         }
29968
29969         function quickselect(arr, k, left, right, compare) {
29970             quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
29971         }
29972
29973         function quickselectStep(arr, k, left, right, compare) {
29974
29975             while (right > left) {
29976                 if (right - left > 600) {
29977                     var n = right - left + 1;
29978                     var m = k - left + 1;
29979                     var z = Math.log(n);
29980                     var s = 0.5 * Math.exp(2 * z / 3);
29981                     var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
29982                     var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
29983                     var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
29984                     quickselectStep(arr, k, newLeft, newRight, compare);
29985                 }
29986
29987                 var t = arr[k];
29988                 var i = left;
29989                 var j = right;
29990
29991                 swap(arr, left, k);
29992                 if (compare(arr[right], t) > 0) swap(arr, left, right);
29993
29994                 while (i < j) {
29995                     swap(arr, i, j);
29996                     i++;
29997                     j--;
29998                     while (compare(arr[i], t) < 0) i++;
29999                     while (compare(arr[j], t) > 0) j--;
30000                 }
30001
30002                 if (compare(arr[left], t) === 0) swap(arr, left, j);
30003                 else {
30004                     j++;
30005                     swap(arr, j, right);
30006                 }
30007
30008                 if (j <= k) left = j + 1;
30009                 if (k <= j) right = j - 1;
30010             }
30011         }
30012
30013         function swap(arr, i, j) {
30014             var tmp = arr[i];
30015             arr[i] = arr[j];
30016             arr[j] = tmp;
30017         }
30018
30019         function defaultCompare(a, b) {
30020             return a < b ? -1 : a > b ? 1 : 0;
30021         }
30022
30023         class RBush {
30024             constructor(maxEntries = 9) {
30025                 // max entries in a node is 9 by default; min node fill is 40% for best performance
30026                 this._maxEntries = Math.max(4, maxEntries);
30027                 this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
30028                 this.clear();
30029             }
30030
30031             all() {
30032                 return this._all(this.data, []);
30033             }
30034
30035             search(bbox) {
30036                 let node = this.data;
30037                 const result = [];
30038
30039                 if (!intersects(bbox, node)) return result;
30040
30041                 const toBBox = this.toBBox;
30042                 const nodesToSearch = [];
30043
30044                 while (node) {
30045                     for (let i = 0; i < node.children.length; i++) {
30046                         const child = node.children[i];
30047                         const childBBox = node.leaf ? toBBox(child) : child;
30048
30049                         if (intersects(bbox, childBBox)) {
30050                             if (node.leaf) result.push(child);
30051                             else if (contains$1(bbox, childBBox)) this._all(child, result);
30052                             else nodesToSearch.push(child);
30053                         }
30054                     }
30055                     node = nodesToSearch.pop();
30056                 }
30057
30058                 return result;
30059             }
30060
30061             collides(bbox) {
30062                 let node = this.data;
30063
30064                 if (!intersects(bbox, node)) return false;
30065
30066                 const nodesToSearch = [];
30067                 while (node) {
30068                     for (let i = 0; i < node.children.length; i++) {
30069                         const child = node.children[i];
30070                         const childBBox = node.leaf ? this.toBBox(child) : child;
30071
30072                         if (intersects(bbox, childBBox)) {
30073                             if (node.leaf || contains$1(bbox, childBBox)) return true;
30074                             nodesToSearch.push(child);
30075                         }
30076                     }
30077                     node = nodesToSearch.pop();
30078                 }
30079
30080                 return false;
30081             }
30082
30083             load(data) {
30084                 if (!(data && data.length)) return this;
30085
30086                 if (data.length < this._minEntries) {
30087                     for (let i = 0; i < data.length; i++) {
30088                         this.insert(data[i]);
30089                     }
30090                     return this;
30091                 }
30092
30093                 // recursively build the tree with the given data from scratch using OMT algorithm
30094                 let node = this._build(data.slice(), 0, data.length - 1, 0);
30095
30096                 if (!this.data.children.length) {
30097                     // save as is if tree is empty
30098                     this.data = node;
30099
30100                 } else if (this.data.height === node.height) {
30101                     // split root if trees have the same height
30102                     this._splitRoot(this.data, node);
30103
30104                 } else {
30105                     if (this.data.height < node.height) {
30106                         // swap trees if inserted one is bigger
30107                         const tmpNode = this.data;
30108                         this.data = node;
30109                         node = tmpNode;
30110                     }
30111
30112                     // insert the small tree into the large tree at appropriate level
30113                     this._insert(node, this.data.height - node.height - 1, true);
30114                 }
30115
30116                 return this;
30117             }
30118
30119             insert(item) {
30120                 if (item) this._insert(item, this.data.height - 1);
30121                 return this;
30122             }
30123
30124             clear() {
30125                 this.data = createNode([]);
30126                 return this;
30127             }
30128
30129             remove(item, equalsFn) {
30130                 if (!item) return this;
30131
30132                 let node = this.data;
30133                 const bbox = this.toBBox(item);
30134                 const path = [];
30135                 const indexes = [];
30136                 let i, parent, goingUp;
30137
30138                 // depth-first iterative tree traversal
30139                 while (node || path.length) {
30140
30141                     if (!node) { // go up
30142                         node = path.pop();
30143                         parent = path[path.length - 1];
30144                         i = indexes.pop();
30145                         goingUp = true;
30146                     }
30147
30148                     if (node.leaf) { // check current node
30149                         const index = findItem(item, node.children, equalsFn);
30150
30151                         if (index !== -1) {
30152                             // item found, remove the item and condense tree upwards
30153                             node.children.splice(index, 1);
30154                             path.push(node);
30155                             this._condense(path);
30156                             return this;
30157                         }
30158                     }
30159
30160                     if (!goingUp && !node.leaf && contains$1(node, bbox)) { // go down
30161                         path.push(node);
30162                         indexes.push(i);
30163                         i = 0;
30164                         parent = node;
30165                         node = node.children[0];
30166
30167                     } else if (parent) { // go right
30168                         i++;
30169                         node = parent.children[i];
30170                         goingUp = false;
30171
30172                     } else node = null; // nothing found
30173                 }
30174
30175                 return this;
30176             }
30177
30178             toBBox(item) { return item; }
30179
30180             compareMinX(a, b) { return a.minX - b.minX; }
30181             compareMinY(a, b) { return a.minY - b.minY; }
30182
30183             toJSON() { return this.data; }
30184
30185             fromJSON(data) {
30186                 this.data = data;
30187                 return this;
30188             }
30189
30190             _all(node, result) {
30191                 const nodesToSearch = [];
30192                 while (node) {
30193                     if (node.leaf) result.push(...node.children);
30194                     else nodesToSearch.push(...node.children);
30195
30196                     node = nodesToSearch.pop();
30197                 }
30198                 return result;
30199             }
30200
30201             _build(items, left, right, height) {
30202
30203                 const N = right - left + 1;
30204                 let M = this._maxEntries;
30205                 let node;
30206
30207                 if (N <= M) {
30208                     // reached leaf level; return leaf
30209                     node = createNode(items.slice(left, right + 1));
30210                     calcBBox(node, this.toBBox);
30211                     return node;
30212                 }
30213
30214                 if (!height) {
30215                     // target height of the bulk-loaded tree
30216                     height = Math.ceil(Math.log(N) / Math.log(M));
30217
30218                     // target number of root entries to maximize storage utilization
30219                     M = Math.ceil(N / Math.pow(M, height - 1));
30220                 }
30221
30222                 node = createNode([]);
30223                 node.leaf = false;
30224                 node.height = height;
30225
30226                 // split the items into M mostly square tiles
30227
30228                 const N2 = Math.ceil(N / M);
30229                 const N1 = N2 * Math.ceil(Math.sqrt(M));
30230
30231                 multiSelect(items, left, right, N1, this.compareMinX);
30232
30233                 for (let i = left; i <= right; i += N1) {
30234
30235                     const right2 = Math.min(i + N1 - 1, right);
30236
30237                     multiSelect(items, i, right2, N2, this.compareMinY);
30238
30239                     for (let j = i; j <= right2; j += N2) {
30240
30241                         const right3 = Math.min(j + N2 - 1, right2);
30242
30243                         // pack each entry recursively
30244                         node.children.push(this._build(items, j, right3, height - 1));
30245                     }
30246                 }
30247
30248                 calcBBox(node, this.toBBox);
30249
30250                 return node;
30251             }
30252
30253             _chooseSubtree(bbox, node, level, path) {
30254                 while (true) {
30255                     path.push(node);
30256
30257                     if (node.leaf || path.length - 1 === level) break;
30258
30259                     let minArea = Infinity;
30260                     let minEnlargement = Infinity;
30261                     let targetNode;
30262
30263                     for (let i = 0; i < node.children.length; i++) {
30264                         const child = node.children[i];
30265                         const area = bboxArea(child);
30266                         const enlargement = enlargedArea(bbox, child) - area;
30267
30268                         // choose entry with the least area enlargement
30269                         if (enlargement < minEnlargement) {
30270                             minEnlargement = enlargement;
30271                             minArea = area < minArea ? area : minArea;
30272                             targetNode = child;
30273
30274                         } else if (enlargement === minEnlargement) {
30275                             // otherwise choose one with the smallest area
30276                             if (area < minArea) {
30277                                 minArea = area;
30278                                 targetNode = child;
30279                             }
30280                         }
30281                     }
30282
30283                     node = targetNode || node.children[0];
30284                 }
30285
30286                 return node;
30287             }
30288
30289             _insert(item, level, isNode) {
30290                 const bbox = isNode ? item : this.toBBox(item);
30291                 const insertPath = [];
30292
30293                 // find the best node for accommodating the item, saving all nodes along the path too
30294                 const node = this._chooseSubtree(bbox, this.data, level, insertPath);
30295
30296                 // put the item into the node
30297                 node.children.push(item);
30298                 extend$1(node, bbox);
30299
30300                 // split on node overflow; propagate upwards if necessary
30301                 while (level >= 0) {
30302                     if (insertPath[level].children.length > this._maxEntries) {
30303                         this._split(insertPath, level);
30304                         level--;
30305                     } else break;
30306                 }
30307
30308                 // adjust bboxes along the insertion path
30309                 this._adjustParentBBoxes(bbox, insertPath, level);
30310             }
30311
30312             // split overflowed node into two
30313             _split(insertPath, level) {
30314                 const node = insertPath[level];
30315                 const M = node.children.length;
30316                 const m = this._minEntries;
30317
30318                 this._chooseSplitAxis(node, m, M);
30319
30320                 const splitIndex = this._chooseSplitIndex(node, m, M);
30321
30322                 const newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
30323                 newNode.height = node.height;
30324                 newNode.leaf = node.leaf;
30325
30326                 calcBBox(node, this.toBBox);
30327                 calcBBox(newNode, this.toBBox);
30328
30329                 if (level) insertPath[level - 1].children.push(newNode);
30330                 else this._splitRoot(node, newNode);
30331             }
30332
30333             _splitRoot(node, newNode) {
30334                 // split root node
30335                 this.data = createNode([node, newNode]);
30336                 this.data.height = node.height + 1;
30337                 this.data.leaf = false;
30338                 calcBBox(this.data, this.toBBox);
30339             }
30340
30341             _chooseSplitIndex(node, m, M) {
30342                 let index;
30343                 let minOverlap = Infinity;
30344                 let minArea = Infinity;
30345
30346                 for (let i = m; i <= M - m; i++) {
30347                     const bbox1 = distBBox(node, 0, i, this.toBBox);
30348                     const bbox2 = distBBox(node, i, M, this.toBBox);
30349
30350                     const overlap = intersectionArea(bbox1, bbox2);
30351                     const area = bboxArea(bbox1) + bboxArea(bbox2);
30352
30353                     // choose distribution with minimum overlap
30354                     if (overlap < minOverlap) {
30355                         minOverlap = overlap;
30356                         index = i;
30357
30358                         minArea = area < minArea ? area : minArea;
30359
30360                     } else if (overlap === minOverlap) {
30361                         // otherwise choose distribution with minimum area
30362                         if (area < minArea) {
30363                             minArea = area;
30364                             index = i;
30365                         }
30366                     }
30367                 }
30368
30369                 return index || M - m;
30370             }
30371
30372             // sorts node children by the best axis for split
30373             _chooseSplitAxis(node, m, M) {
30374                 const compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
30375                 const compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
30376                 const xMargin = this._allDistMargin(node, m, M, compareMinX);
30377                 const yMargin = this._allDistMargin(node, m, M, compareMinY);
30378
30379                 // if total distributions margin value is minimal for x, sort by minX,
30380                 // otherwise it's already sorted by minY
30381                 if (xMargin < yMargin) node.children.sort(compareMinX);
30382             }
30383
30384             // total margin of all possible split distributions where each node is at least m full
30385             _allDistMargin(node, m, M, compare) {
30386                 node.children.sort(compare);
30387
30388                 const toBBox = this.toBBox;
30389                 const leftBBox = distBBox(node, 0, m, toBBox);
30390                 const rightBBox = distBBox(node, M - m, M, toBBox);
30391                 let margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
30392
30393                 for (let i = m; i < M - m; i++) {
30394                     const child = node.children[i];
30395                     extend$1(leftBBox, node.leaf ? toBBox(child) : child);
30396                     margin += bboxMargin(leftBBox);
30397                 }
30398
30399                 for (let i = M - m - 1; i >= m; i--) {
30400                     const child = node.children[i];
30401                     extend$1(rightBBox, node.leaf ? toBBox(child) : child);
30402                     margin += bboxMargin(rightBBox);
30403                 }
30404
30405                 return margin;
30406             }
30407
30408             _adjustParentBBoxes(bbox, path, level) {
30409                 // adjust bboxes along the given tree path
30410                 for (let i = level; i >= 0; i--) {
30411                     extend$1(path[i], bbox);
30412                 }
30413             }
30414
30415             _condense(path) {
30416                 // go through the path, removing empty nodes and updating bboxes
30417                 for (let i = path.length - 1, siblings; i >= 0; i--) {
30418                     if (path[i].children.length === 0) {
30419                         if (i > 0) {
30420                             siblings = path[i - 1].children;
30421                             siblings.splice(siblings.indexOf(path[i]), 1);
30422
30423                         } else this.clear();
30424
30425                     } else calcBBox(path[i], this.toBBox);
30426                 }
30427             }
30428         }
30429
30430         function findItem(item, items, equalsFn) {
30431             if (!equalsFn) return items.indexOf(item);
30432
30433             for (let i = 0; i < items.length; i++) {
30434                 if (equalsFn(item, items[i])) return i;
30435             }
30436             return -1;
30437         }
30438
30439         // calculate node's bbox from bboxes of its children
30440         function calcBBox(node, toBBox) {
30441             distBBox(node, 0, node.children.length, toBBox, node);
30442         }
30443
30444         // min bounding rectangle of node children from k to p-1
30445         function distBBox(node, k, p, toBBox, destNode) {
30446             if (!destNode) destNode = createNode(null);
30447             destNode.minX = Infinity;
30448             destNode.minY = Infinity;
30449             destNode.maxX = -Infinity;
30450             destNode.maxY = -Infinity;
30451
30452             for (let i = k; i < p; i++) {
30453                 const child = node.children[i];
30454                 extend$1(destNode, node.leaf ? toBBox(child) : child);
30455             }
30456
30457             return destNode;
30458         }
30459
30460         function extend$1(a, b) {
30461             a.minX = Math.min(a.minX, b.minX);
30462             a.minY = Math.min(a.minY, b.minY);
30463             a.maxX = Math.max(a.maxX, b.maxX);
30464             a.maxY = Math.max(a.maxY, b.maxY);
30465             return a;
30466         }
30467
30468         function compareNodeMinX(a, b) { return a.minX - b.minX; }
30469         function compareNodeMinY(a, b) { return a.minY - b.minY; }
30470
30471         function bboxArea(a)   { return (a.maxX - a.minX) * (a.maxY - a.minY); }
30472         function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
30473
30474         function enlargedArea(a, b) {
30475             return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
30476                    (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
30477         }
30478
30479         function intersectionArea(a, b) {
30480             const minX = Math.max(a.minX, b.minX);
30481             const minY = Math.max(a.minY, b.minY);
30482             const maxX = Math.min(a.maxX, b.maxX);
30483             const maxY = Math.min(a.maxY, b.maxY);
30484
30485             return Math.max(0, maxX - minX) *
30486                    Math.max(0, maxY - minY);
30487         }
30488
30489         function contains$1(a, b) {
30490             return a.minX <= b.minX &&
30491                    a.minY <= b.minY &&
30492                    b.maxX <= a.maxX &&
30493                    b.maxY <= a.maxY;
30494         }
30495
30496         function intersects(a, b) {
30497             return b.minX <= a.maxX &&
30498                    b.minY <= a.maxY &&
30499                    b.maxX >= a.minX &&
30500                    b.maxY >= a.minY;
30501         }
30502
30503         function createNode(children) {
30504             return {
30505                 children,
30506                 height: 1,
30507                 leaf: true,
30508                 minX: Infinity,
30509                 minY: Infinity,
30510                 maxX: -Infinity,
30511                 maxY: -Infinity
30512             };
30513         }
30514
30515         // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
30516         // combines selection algorithm with binary divide & conquer approach
30517
30518         function multiSelect(arr, left, right, n, compare) {
30519             const stack = [left, right];
30520
30521             while (stack.length) {
30522                 right = stack.pop();
30523                 left = stack.pop();
30524
30525                 if (right - left <= n) continue;
30526
30527                 const mid = left + Math.ceil((right - left) / n / 2) * n;
30528                 quickselect(arr, mid, left, right, compare);
30529
30530                 stack.push(left, mid, mid, right);
30531             }
30532         }
30533
30534         const tiler = utilTiler();
30535         const dispatch$1 = dispatch('loaded');
30536         const _tileZoom = 14;
30537         const _krUrlRoot = 'https://www.keepright.at';
30538         let _krData = { errorTypes: {}, localizeStrings: {} };
30539
30540         // This gets reassigned if reset
30541         let _cache;
30542
30543         const _krRuleset = [
30544           // no 20 - multiple node on same spot - these are mostly boundaries overlapping roads
30545           30, 40, 50, 60, 70, 90, 100, 110, 120, 130, 150, 160, 170, 180,
30546           190, 191, 192, 193, 194, 195, 196, 197, 198,
30547           200, 201, 202, 203, 204, 205, 206, 207, 208, 210, 220,
30548           230, 231, 232, 270, 280, 281, 282, 283, 284, 285,
30549           290, 291, 292, 293, 294, 295, 296, 297, 298, 300, 310, 311, 312, 313,
30550           320, 350, 360, 370, 380, 390, 400, 401, 402, 410, 411, 412, 413
30551         ];
30552
30553
30554         function abortRequest(controller) {
30555           if (controller) {
30556             controller.abort();
30557           }
30558         }
30559
30560         function abortUnwantedRequests(cache, tiles) {
30561           Object.keys(cache.inflightTile).forEach(k => {
30562             const wanted = tiles.find(tile => k === tile.id);
30563             if (!wanted) {
30564               abortRequest(cache.inflightTile[k]);
30565               delete cache.inflightTile[k];
30566             }
30567           });
30568         }
30569
30570
30571         function encodeIssueRtree(d) {
30572           return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
30573         }
30574
30575
30576         // Replace or remove QAItem from rtree
30577         function updateRtree(item, replace) {
30578           _cache.rtree.remove(item, (a, b) => a.data.id === b.data.id);
30579
30580           if (replace) {
30581             _cache.rtree.insert(item);
30582           }
30583         }
30584
30585
30586         function tokenReplacements(d) {
30587           if (!(d instanceof QAItem)) return;
30588
30589           const htmlRegex = new RegExp(/<\/[a-z][\s\S]*>/);
30590           const replacements = {};
30591
30592           const issueTemplate = _krData.errorTypes[d.whichType];
30593           if (!issueTemplate) {
30594             /* eslint-disable no-console */
30595             console.log('No Template: ', d.whichType);
30596             console.log('  ', d.description);
30597             /* eslint-enable no-console */
30598             return;
30599           }
30600
30601           // some descriptions are just fixed text
30602           if (!issueTemplate.regex) return;
30603
30604           // regex pattern should match description with variable details captured
30605           const errorRegex = new RegExp(issueTemplate.regex, 'i');
30606           const errorMatch = errorRegex.exec(d.description);
30607           if (!errorMatch) {
30608             /* eslint-disable no-console */
30609             console.log('Unmatched: ', d.whichType);
30610             console.log('  ', d.description);
30611             console.log('  ', errorRegex);
30612             /* eslint-enable no-console */
30613             return;
30614           }
30615
30616           for (let i = 1; i < errorMatch.length; i++) {   // skip first
30617             let capture = errorMatch[i];
30618             let idType;
30619
30620             idType = 'IDs' in issueTemplate ? issueTemplate.IDs[i-1] : '';
30621             if (idType && capture) {   // link IDs if present in the capture
30622               capture = parseError(capture, idType);
30623             } else if (htmlRegex.test(capture)) {   // escape any html in non-IDs
30624               capture = '\\' +  capture + '\\';
30625             } else {
30626               const compare = capture.toLowerCase();
30627               if (_krData.localizeStrings[compare]) {   // some replacement strings can be localized
30628                 capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
30629               }
30630             }
30631
30632             replacements['var' + i] = capture;
30633           }
30634
30635           return replacements;
30636         }
30637
30638
30639         function parseError(capture, idType) {
30640           const compare = capture.toLowerCase();
30641           if (_krData.localizeStrings[compare]) {   // some replacement strings can be localized
30642             capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
30643           }
30644
30645           switch (idType) {
30646             // link a string like "this node"
30647             case 'this':
30648               capture = linkErrorObject(capture);
30649               break;
30650
30651             case 'url':
30652               capture = linkURL(capture);
30653               break;
30654
30655             // link an entity ID
30656             case 'n':
30657             case 'w':
30658             case 'r':
30659               capture = linkEntity(idType + capture);
30660               break;
30661
30662             // some errors have more complex ID lists/variance
30663             case '20':
30664               capture = parse20(capture);
30665               break;
30666             case '211':
30667               capture = parse211(capture);
30668               break;
30669             case '231':
30670               capture = parse231(capture);
30671               break;
30672             case '294':
30673               capture = parse294(capture);
30674               break;
30675             case '370':
30676               capture = parse370(capture);
30677               break;
30678           }
30679
30680           return capture;
30681
30682
30683           function linkErrorObject(d) {
30684             return `<a class="error_object_link">${d}</a>`;
30685           }
30686
30687           function linkEntity(d) {
30688             return `<a class="error_entity_link">${d}</a>`;
30689           }
30690
30691           function linkURL(d) {
30692             return `<a class="kr_external_link" target="_blank" href="${d}">${d}</a>`;
30693           }
30694
30695           // arbitrary node list of form: #ID, #ID, #ID...
30696           function parse211(capture) {
30697             let newList = [];
30698             const items = capture.split(', ');
30699
30700             items.forEach(item => {
30701               // ID has # at the front
30702               let id = linkEntity('n' + item.slice(1));
30703               newList.push(id);
30704             });
30705
30706             return newList.join(', ');
30707           }
30708
30709           // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)...
30710           function parse231(capture) {
30711             let newList = [];
30712             // unfortunately 'layer' can itself contain commas, so we split on '),'
30713             const items = capture.split('),');
30714
30715             items.forEach(item => {
30716               const match = item.match(/\#(\d+)\((.+)\)?/);
30717               if (match !== null && match.length > 2) {
30718                 newList.push(linkEntity('w' + match[1]) + ' ' +
30719                   _t('QA.keepRight.errorTypes.231.layer', { layer: match[2] })
30720                 );
30721               }
30722             });
30723
30724             return newList.join(', ');
30725           }
30726
30727           // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID...
30728           function parse294(capture) {
30729             let newList = [];
30730             const items = capture.split(',');
30731
30732             items.forEach(item => {
30733               // item of form "from/to node/relation #ID"
30734               item = item.split(' ');
30735
30736               // to/from role is more clear in quotes
30737               const role = `"${item[0]}"`;
30738
30739               // first letter of node/relation provides the type
30740               const idType = item[1].slice(0,1);
30741
30742               // ID has # at the front
30743               let id = item[2].slice(1);
30744               id = linkEntity(idType + id);
30745
30746               newList.push(`${role} ${item[1]} ${id}`);
30747             });
30748
30749             return newList.join(', ');
30750           }
30751
30752           // may or may not include the string "(including the name 'name')"
30753           function parse370(capture) {
30754             if (!capture) return '';
30755
30756             const match = capture.match(/\(including the name (\'.+\')\)/);
30757             if (match && match.length) {
30758               return _t('QA.keepRight.errorTypes.370.including_the_name', { name: match[1] });
30759             }
30760             return '';
30761           }
30762
30763           // arbitrary node list of form: #ID,#ID,#ID...
30764           function parse20(capture) {
30765             let newList = [];
30766             const items = capture.split(',');
30767
30768             items.forEach(item => {
30769               // ID has # at the front
30770               const id = linkEntity('n' + item.slice(1));
30771               newList.push(id);
30772             });
30773
30774             return newList.join(', ');
30775           }
30776         }
30777
30778
30779         var serviceKeepRight = {
30780           title: 'keepRight',
30781
30782           init() {
30783             _mainFileFetcher.get('keepRight')
30784               .then(d => _krData = d);
30785
30786             if (!_cache) {
30787               this.reset();
30788             }
30789
30790             this.event = utilRebind(this, dispatch$1, 'on');
30791           },
30792
30793           reset() {
30794             if (_cache) {
30795               Object.values(_cache.inflightTile).forEach(abortRequest);
30796             }
30797
30798             _cache = {
30799               data: {},
30800               loadedTile: {},
30801               inflightTile: {},
30802               inflightPost: {},
30803               closed: {},
30804               rtree: new RBush()
30805             };
30806           },
30807
30808
30809           // KeepRight API:  http://osm.mueschelsoft.de/keepright/interfacing.php
30810           loadIssues(projection) {
30811             const options = {
30812               format: 'geojson',
30813               ch: _krRuleset
30814             };
30815
30816             // determine the needed tiles to cover the view
30817             const tiles = tiler
30818               .zoomExtent([_tileZoom, _tileZoom])
30819               .getTiles(projection);
30820
30821             // abort inflight requests that are no longer needed
30822             abortUnwantedRequests(_cache, tiles);
30823
30824             // issue new requests..
30825             tiles.forEach(tile => {
30826               if (_cache.loadedTile[tile.id] || _cache.inflightTile[tile.id]) return;
30827
30828               const [ left, top, right, bottom ] = tile.extent.rectangle();
30829               const params = Object.assign({}, options, { left, bottom, right, top });
30830               const url = `${_krUrlRoot}/export.php?` + utilQsString(params);
30831               const controller = new AbortController();
30832
30833               _cache.inflightTile[tile.id] = controller;
30834
30835               d3_json(url, { signal: controller.signal })
30836                 .then(data => {
30837                   delete _cache.inflightTile[tile.id];
30838                   _cache.loadedTile[tile.id] = true;
30839                   if (!data || !data.features || !data.features.length) {
30840                     throw new Error('No Data');
30841                   }
30842
30843                   data.features.forEach(feature => {
30844                     const {
30845                       properties: {
30846                         error_type: itemType,
30847                         error_id: id,
30848                         comment = null,
30849                         object_id: objectId,
30850                         object_type: objectType,
30851                         schema,
30852                         title
30853                       }
30854                     } = feature;
30855                     let {
30856                       geometry: { coordinates: loc },
30857                       properties: { description = '' }
30858                     } = feature;
30859
30860                     // if there is a parent, save its error type e.g.:
30861                     //  Error 191 = "highway-highway"
30862                     //  Error 190 = "intersections without junctions"  (parent)
30863                     const issueTemplate = _krData.errorTypes[itemType];
30864                     const parentIssueType = (Math.floor(itemType / 10) * 10).toString();
30865
30866                     // try to handle error type directly, fallback to parent error type.
30867                     const whichType = issueTemplate ? itemType : parentIssueType;
30868                     const whichTemplate = _krData.errorTypes[whichType];
30869
30870                     // Rewrite a few of the errors at this point..
30871                     // This is done to make them easier to linkify and translate.
30872                     switch (whichType) {
30873                       case '170':
30874                         description = `This feature has a FIXME tag: ${description}`;
30875                         break;
30876                       case '292':
30877                       case '293':
30878                         description = description.replace('A turn-', 'This turn-');
30879                         break;
30880                       case '294':
30881                       case '295':
30882                       case '296':
30883                       case '297':
30884                       case '298':
30885                         description = `This turn-restriction~${description}`;
30886                         break;
30887                       case '300':
30888                         description = 'This highway is missing a maxspeed tag';
30889                         break;
30890                       case '411':
30891                       case '412':
30892                       case '413':
30893                         description = `This feature~${description}`;
30894                         break;
30895                     }
30896
30897                     // move markers slightly so it doesn't obscure the geometry,
30898                     // then move markers away from other coincident markers
30899                     let coincident = false;
30900                     do {
30901                       // first time, move marker up. after that, move marker right.
30902                       let delta = coincident ? [0.00001, 0] : [0, 0.00001];
30903                       loc = geoVecAdd(loc, delta);
30904                       let bbox = geoExtent(loc).bbox();
30905                       coincident = _cache.rtree.search(bbox).length;
30906                     } while (coincident);
30907
30908                     let d = new QAItem(loc, this, itemType, id, {
30909                       comment,
30910                       description,
30911                       whichType,
30912                       parentIssueType,
30913                       severity: whichTemplate.severity || 'error',
30914                       objectId,
30915                       objectType,
30916                       schema,
30917                       title
30918                     });
30919
30920                     d.replacements = tokenReplacements(d);
30921
30922                     _cache.data[id] = d;
30923                     _cache.rtree.insert(encodeIssueRtree(d));
30924                   });
30925
30926                   dispatch$1.call('loaded');
30927                 })
30928                 .catch(() => {
30929                   delete _cache.inflightTile[tile.id];
30930                   _cache.loadedTile[tile.id] = true;
30931                 });
30932
30933             });
30934           },
30935
30936
30937           postUpdate(d, callback) {
30938             if (_cache.inflightPost[d.id]) {
30939               return callback({ message: 'Error update already inflight', status: -2 }, d);
30940             }
30941
30942             const params = { schema: d.schema, id: d.id };
30943
30944             if (d.newStatus) {
30945               params.st = d.newStatus;
30946             }
30947             if (d.newComment !== undefined) {
30948               params.co = d.newComment;
30949             }
30950
30951             // NOTE: This throws a CORS err, but it seems successful.
30952             // We don't care too much about the response, so this is fine.
30953             const url = `${_krUrlRoot}/comment.php?` + utilQsString(params);
30954             const controller = new AbortController();
30955
30956             _cache.inflightPost[d.id] = controller;
30957
30958             // Since this is expected to throw an error just continue as if it worked
30959             // (worst case scenario the request truly fails and issue will show up if iD restarts)
30960             d3_json(url, { signal: controller.signal })
30961               .finally(() => {
30962                 delete _cache.inflightPost[d.id];
30963
30964                 if (d.newStatus === 'ignore') {
30965                   // ignore permanently (false positive)
30966                   this.removeItem(d);
30967                 } else if (d.newStatus === 'ignore_t') {
30968                   // ignore temporarily (error fixed)
30969                   this.removeItem(d);
30970                   _cache.closed[`${d.schema}:${d.id}`] = true;
30971                 } else {
30972                   d = this.replaceItem(d.update({
30973                     comment: d.newComment,
30974                     newComment: undefined,
30975                     newState: undefined
30976                   }));
30977                 }
30978
30979                 if (callback) callback(null, d);
30980               });
30981           },
30982
30983           // Get all cached QAItems covering the viewport
30984           getItems(projection) {
30985             const viewport = projection.clipExtent();
30986             const min = [viewport[0][0], viewport[1][1]];
30987             const max = [viewport[1][0], viewport[0][1]];
30988             const bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
30989
30990             return _cache.rtree.search(bbox).map(d => d.data);
30991           },
30992
30993           // Get a QAItem from cache
30994           // NOTE: Don't change method name until UI v3 is merged
30995           getError(id) {
30996             return _cache.data[id];
30997           },
30998
30999           // Replace a single QAItem in the cache
31000           replaceItem(item) {
31001             if (!(item instanceof QAItem) || !item.id) return;
31002
31003             _cache.data[item.id] = item;
31004             updateRtree(encodeIssueRtree(item), true); // true = replace
31005             return item;
31006           },
31007
31008           // Remove a single QAItem from the cache
31009           removeItem(item) {
31010             if (!(item instanceof QAItem) || !item.id) return;
31011
31012             delete _cache.data[item.id];
31013             updateRtree(encodeIssueRtree(item), false); // false = remove
31014           },
31015
31016           issueURL(item) {
31017             return `${_krUrlRoot}/report_map.php?schema=${item.schema}&error=${item.id}`;
31018           },
31019
31020           // Get an array of issues closed during this session.
31021           // Used to populate `closed:keepright` changeset tag
31022           getClosedIDs() {
31023             return Object.keys(_cache.closed).sort();
31024           }
31025
31026         };
31027
31028         const tiler$1 = utilTiler();
31029         const dispatch$2 = dispatch('loaded');
31030         const _tileZoom$1 = 14;
31031         const _impOsmUrls = {
31032           ow: 'https://grab.community.improve-osm.org/directionOfFlowService',
31033           mr: 'https://grab.community.improve-osm.org/missingGeoService',
31034           tr: 'https://grab.community.improve-osm.org/turnRestrictionService'
31035         };
31036         let _impOsmData = { icons: {} };
31037
31038
31039         // This gets reassigned if reset
31040         let _cache$1;
31041
31042         function abortRequest$1(i) {
31043           Object.values(i).forEach(controller => {
31044             if (controller) {
31045               controller.abort();
31046             }
31047           });
31048         }
31049
31050         function abortUnwantedRequests$1(cache, tiles) {
31051           Object.keys(cache.inflightTile).forEach(k => {
31052             const wanted = tiles.find(tile => k === tile.id);
31053             if (!wanted) {
31054               abortRequest$1(cache.inflightTile[k]);
31055               delete cache.inflightTile[k];
31056             }
31057           });
31058         }
31059
31060         function encodeIssueRtree$1(d) {
31061           return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
31062         }
31063
31064         // Replace or remove QAItem from rtree
31065         function updateRtree$1(item, replace) {
31066           _cache$1.rtree.remove(item, (a, b) => a.data.id === b.data.id);
31067
31068           if (replace) {
31069             _cache$1.rtree.insert(item);
31070           }
31071         }
31072
31073         function linkErrorObject(d) {
31074           return `<a class="error_object_link">${d}</a>`;
31075         }
31076
31077         function linkEntity(d) {
31078           return `<a class="error_entity_link">${d}</a>`;
31079         }
31080
31081         function pointAverage(points) {
31082           if (points.length) {
31083             const sum = points.reduce(
31084               (acc, point) => geoVecAdd(acc, [point.lon, point.lat]),
31085               [0,0]
31086             );
31087             return geoVecScale(sum, 1 / points.length);
31088           } else {
31089             return [0,0];
31090           }
31091         }
31092
31093         function relativeBearing(p1, p2) {
31094           let angle = Math.atan2(p2.lon - p1.lon, p2.lat - p1.lat);
31095           if (angle < 0) {
31096             angle += 2 * Math.PI;
31097           }
31098
31099           // Return degrees
31100           return angle * 180 / Math.PI;
31101         }
31102
31103         // Assuming range [0,360)
31104         function cardinalDirection(bearing) {
31105           const dir = 45 * Math.round(bearing / 45);
31106           const compass = {
31107             0: 'north',
31108             45: 'northeast',
31109             90: 'east',
31110             135: 'southeast',
31111             180: 'south',
31112             225: 'southwest',
31113             270: 'west',
31114             315: 'northwest',
31115             360: 'north'
31116           };
31117
31118           return _t(`QA.improveOSM.directions.${compass[dir]}`);
31119         }
31120
31121         // Errors shouldn't obscure eachother
31122         function preventCoincident(loc, bumpUp) {
31123           let coincident = false;
31124           do {
31125             // first time, move marker up. after that, move marker right.
31126             let delta = coincident ? [0.00001, 0] : (bumpUp ? [0, 0.00001] : [0, 0]);
31127             loc = geoVecAdd(loc, delta);
31128             let bbox = geoExtent(loc).bbox();
31129             coincident = _cache$1.rtree.search(bbox).length;
31130           } while (coincident);
31131
31132           return loc;
31133         }
31134
31135         var serviceImproveOSM = {
31136           title: 'improveOSM',
31137
31138           init() {
31139             _mainFileFetcher.get('qa_data')
31140               .then(d => _impOsmData = d.improveOSM);
31141
31142             if (!_cache$1) {
31143               this.reset();
31144             }
31145
31146             this.event = utilRebind(this, dispatch$2, 'on');
31147           },
31148
31149           reset() {
31150             if (_cache$1) {
31151               Object.values(_cache$1.inflightTile).forEach(abortRequest$1);
31152             }
31153             _cache$1 = {
31154               data: {},
31155               loadedTile: {},
31156               inflightTile: {},
31157               inflightPost: {},
31158               closed: {},
31159               rtree: new RBush()
31160             };
31161           },
31162
31163           loadIssues(projection) {
31164             const options = {
31165               client: 'iD',
31166               status: 'OPEN',
31167               zoom: '19' // Use a high zoom so that clusters aren't returned
31168             };
31169
31170             // determine the needed tiles to cover the view
31171             const tiles = tiler$1
31172               .zoomExtent([_tileZoom$1, _tileZoom$1])
31173               .getTiles(projection);
31174
31175             // abort inflight requests that are no longer needed
31176             abortUnwantedRequests$1(_cache$1, tiles);
31177
31178             // issue new requests..
31179             tiles.forEach(tile => {
31180               if (_cache$1.loadedTile[tile.id] || _cache$1.inflightTile[tile.id]) return;
31181
31182               const [ east, north, west, south ] = tile.extent.rectangle();
31183               const params = Object.assign({}, options, { east, south, west, north });
31184
31185               // 3 separate requests to store for each tile
31186               const requests = {};
31187
31188               Object.keys(_impOsmUrls).forEach(k => {
31189                 // We exclude WATER from missing geometry as it doesn't seem useful
31190                 // We use most confident one-way and turn restrictions only, still have false positives
31191                 const kParams = Object.assign({},
31192                   params,
31193                   (k === 'mr') ? { type: 'PARKING,ROAD,BOTH,PATH' } : { confidenceLevel: 'C1' }
31194                 );
31195                 const url = `${_impOsmUrls[k]}/search?` + utilQsString(kParams);
31196                 const controller = new AbortController();
31197
31198                 requests[k] = controller;
31199
31200                 d3_json(url, { signal: controller.signal })
31201                   .then(data => {
31202                     delete _cache$1.inflightTile[tile.id][k];
31203                     if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
31204                       delete _cache$1.inflightTile[tile.id];
31205                       _cache$1.loadedTile[tile.id] = true;
31206                     }
31207
31208                     // Road segments at high zoom == oneways
31209                     if (data.roadSegments) {
31210                       data.roadSegments.forEach(feature => {
31211                         // Position error at the approximate middle of the segment
31212                         const { points, wayId, fromNodeId, toNodeId } = feature;
31213                         const itemId = `${wayId}${fromNodeId}${toNodeId}`;
31214                         let mid = points.length / 2;
31215                         let loc;
31216
31217                         // Even number of points, find midpoint of the middle two
31218                         // Odd number of points, use position of very middle point
31219                         if (mid % 1 === 0) {
31220                           loc = pointAverage([points[mid - 1], points[mid]]);
31221                         } else {
31222                           mid = points[Math.floor(mid)];
31223                           loc = [mid.lon, mid.lat];
31224                         }
31225
31226                         // One-ways can land on same segment in opposite direction
31227                         loc = preventCoincident(loc, false);
31228
31229                         let d = new QAItem(loc, this, k, itemId, {
31230                           issueKey: k, // used as a category
31231                           identifier: { // used to post changes
31232                             wayId,
31233                             fromNodeId,
31234                             toNodeId
31235                           },
31236                           objectId: wayId,
31237                           objectType: 'way'
31238                         });
31239
31240                         // Variables used in the description
31241                         d.replacements = {
31242                           percentage: feature.percentOfTrips,
31243                           num_trips: feature.numberOfTrips,
31244                           highway: linkErrorObject(_t('QA.keepRight.error_parts.highway')),
31245                           from_node: linkEntity('n' + feature.fromNodeId),
31246                           to_node: linkEntity('n' + feature.toNodeId)
31247                         };
31248
31249                         _cache$1.data[d.id] = d;
31250                         _cache$1.rtree.insert(encodeIssueRtree$1(d));
31251                       });
31252                     }
31253
31254                     // Tiles at high zoom == missing roads
31255                     if (data.tiles) {
31256                       data.tiles.forEach(feature => {
31257                         const { type, x, y, numberOfTrips } = feature;
31258                         const geoType = type.toLowerCase();
31259                         const itemId = `${geoType}${x}${y}${numberOfTrips}`;
31260
31261                         // Average of recorded points should land on the missing geometry
31262                         // Missing geometry could happen to land on another error
31263                         let loc = pointAverage(feature.points);
31264                         loc = preventCoincident(loc, false);
31265
31266                         let d = new QAItem(loc, this, `${k}-${geoType}`, itemId, {
31267                           issueKey: k,
31268                           identifier: { x, y }
31269                         });
31270
31271                         d.replacements = {
31272                           num_trips: numberOfTrips,
31273                           geometry_type: _t(`QA.improveOSM.geometry_types.${geoType}`)
31274                         };
31275
31276                         // -1 trips indicates data came from a 3rd party
31277                         if (numberOfTrips === -1) {
31278                           d.desc = _t('QA.improveOSM.error_types.mr.description_alt', d.replacements);
31279                         }
31280
31281                         _cache$1.data[d.id] = d;
31282                         _cache$1.rtree.insert(encodeIssueRtree$1(d));
31283                       });
31284                     }
31285
31286                     // Entities at high zoom == turn restrictions
31287                     if (data.entities) {
31288                       data.entities.forEach(feature => {
31289                         const { point, id, segments, numberOfPasses, turnType } = feature;
31290                         const itemId = `${id.replace(/[,:+#]/g, '_')}`;
31291
31292                         // Turn restrictions could be missing at same junction
31293                         // We also want to bump the error up so node is accessible
31294                         const loc = preventCoincident([point.lon, point.lat], true);
31295
31296                         // Elements are presented in a strange way
31297                         const ids = id.split(',');
31298                         const from_way = ids[0];
31299                         const via_node = ids[3];
31300                         const to_way = ids[2].split(':')[1];
31301
31302                         let d = new QAItem(loc, this, k, itemId, {
31303                           issueKey: k,
31304                           identifier: id,
31305                           objectId: via_node,
31306                           objectType: 'node'
31307                         });
31308
31309                         // Travel direction along from_way clarifies the turn restriction
31310                         const [ p1, p2 ] = segments[0].points;
31311                         const dir_of_travel = cardinalDirection(relativeBearing(p1, p2));
31312
31313                         // Variables used in the description
31314                         d.replacements = {
31315                           num_passed: numberOfPasses,
31316                           num_trips: segments[0].numberOfTrips,
31317                           turn_restriction: turnType.toLowerCase(),
31318                           from_way: linkEntity('w' + from_way),
31319                           to_way: linkEntity('w' + to_way),
31320                           travel_direction: dir_of_travel,
31321                           junction: linkErrorObject(_t('QA.keepRight.error_parts.this_node'))
31322                         };
31323
31324                         _cache$1.data[d.id] = d;
31325                         _cache$1.rtree.insert(encodeIssueRtree$1(d));
31326                         dispatch$2.call('loaded');
31327                       });
31328                     }
31329                   })
31330                   .catch(() => {
31331                     delete _cache$1.inflightTile[tile.id][k];
31332                     if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
31333                       delete _cache$1.inflightTile[tile.id];
31334                       _cache$1.loadedTile[tile.id] = true;
31335                     }
31336                   });
31337               });
31338
31339               _cache$1.inflightTile[tile.id] = requests;
31340             });
31341           },
31342
31343           getComments(item) {
31344             // If comments already retrieved no need to do so again
31345             if (item.comments) {
31346               return Promise.resolve(item);
31347             }
31348
31349             const key = item.issueKey;
31350             let qParams = {};
31351
31352             if (key === 'ow') {
31353               qParams = item.identifier;
31354             } else if (key === 'mr') {
31355               qParams.tileX = item.identifier.x;
31356               qParams.tileY = item.identifier.y;
31357             } else if (key === 'tr') {
31358               qParams.targetId = item.identifier;
31359             }
31360
31361             const url = `${_impOsmUrls[key]}/retrieveComments?` + utilQsString(qParams);
31362             const cacheComments = data => {
31363               // Assign directly for immediate use afterwards
31364               // comments are served newest to oldest
31365               item.comments = data.comments ? data.comments.reverse() : [];
31366               this.replaceItem(item);
31367             };
31368
31369             return d3_json(url).then(cacheComments).then(() => item);
31370           },
31371
31372           postUpdate(d, callback) {
31373             if (!serviceOsm.authenticated()) { // Username required in payload
31374               return callback({ message: 'Not Authenticated', status: -3}, d);
31375             }
31376             if (_cache$1.inflightPost[d.id]) {
31377               return callback({ message: 'Error update already inflight', status: -2 }, d);
31378             }
31379
31380             // Payload can only be sent once username is established
31381             serviceOsm.userDetails(sendPayload.bind(this));
31382
31383             function sendPayload(err, user) {
31384               if (err) { return callback(err, d); }
31385
31386               const key = d.issueKey;
31387               const url = `${_impOsmUrls[key]}/comment`;
31388               const payload = {
31389                 username: user.display_name,
31390                 targetIds: [ d.identifier ]
31391               };
31392
31393               if (d.newStatus) {
31394                 payload.status = d.newStatus;
31395                 payload.text = 'status changed';
31396               }
31397
31398               // Comment take place of default text
31399               if (d.newComment) {
31400                 payload.text = d.newComment;
31401               }
31402
31403               const controller = new AbortController();
31404               _cache$1.inflightPost[d.id] = controller;
31405
31406               const options = {
31407                 method: 'POST',
31408                 signal: controller.signal,
31409                 body: JSON.stringify(payload)
31410               };
31411
31412               d3_json(url, options)
31413                 .then(() => {
31414                   delete _cache$1.inflightPost[d.id];
31415
31416                   // Just a comment, update error in cache
31417                   if (!d.newStatus) {
31418                     const now = new Date();
31419                     let comments = d.comments ? d.comments : [];
31420
31421                     comments.push({
31422                       username: payload.username,
31423                       text: payload.text,
31424                       timestamp: now.getTime() / 1000
31425                     });
31426
31427                     this.replaceItem(d.update({
31428                       comments: comments,
31429                       newComment: undefined
31430                     }));
31431                   } else {
31432                     this.removeItem(d);
31433                     if (d.newStatus === 'SOLVED') {
31434                       // Keep track of the number of issues closed per type to tag the changeset
31435                       if (!(d.issueKey in _cache$1.closed)) {
31436                         _cache$1.closed[d.issueKey] = 0;
31437                       }
31438                       _cache$1.closed[d.issueKey] += 1;
31439                     }
31440                   }
31441                   if (callback) callback(null, d);
31442                 })
31443                 .catch(err => {
31444                   delete _cache$1.inflightPost[d.id];
31445                   if (callback) callback(err.message);
31446                 });
31447             }
31448           },
31449
31450
31451           // Get all cached QAItems covering the viewport
31452           getItems(projection) {
31453             const viewport = projection.clipExtent();
31454             const min = [viewport[0][0], viewport[1][1]];
31455             const max = [viewport[1][0], viewport[0][1]];
31456             const bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
31457
31458             return _cache$1.rtree.search(bbox).map(d => d.data);
31459           },
31460
31461           // Get a QAItem from cache
31462           // NOTE: Don't change method name until UI v3 is merged
31463           getError(id) {
31464             return _cache$1.data[id];
31465           },
31466
31467           // get the name of the icon to display for this item
31468           getIcon(itemType) {
31469             return _impOsmData.icons[itemType];
31470           },
31471
31472           // Replace a single QAItem in the cache
31473           replaceItem(issue) {
31474             if (!(issue instanceof QAItem) || !issue.id) return;
31475
31476             _cache$1.data[issue.id] = issue;
31477             updateRtree$1(encodeIssueRtree$1(issue), true); // true = replace
31478             return issue;
31479           },
31480
31481           // Remove a single QAItem from the cache
31482           removeItem(issue) {
31483             if (!(issue instanceof QAItem) || !issue.id) return;
31484
31485             delete _cache$1.data[issue.id];
31486             updateRtree$1(encodeIssueRtree$1(issue), false); // false = remove
31487           },
31488
31489           // Used to populate `closed:improveosm:*` changeset tags
31490           getClosedCounts() {
31491             return _cache$1.closed;
31492           }
31493         };
31494
31495         var defaults = createCommonjsModule(function (module) {
31496         function getDefaults() {
31497           return {
31498             baseUrl: null,
31499             breaks: false,
31500             gfm: true,
31501             headerIds: true,
31502             headerPrefix: '',
31503             highlight: null,
31504             langPrefix: 'language-',
31505             mangle: true,
31506             pedantic: false,
31507             renderer: null,
31508             sanitize: false,
31509             sanitizer: null,
31510             silent: false,
31511             smartLists: false,
31512             smartypants: false,
31513             tokenizer: null,
31514             xhtml: false
31515           };
31516         }
31517
31518         function changeDefaults(newDefaults) {
31519           module.exports.defaults = newDefaults;
31520         }
31521
31522         module.exports = {
31523           defaults: getDefaults(),
31524           getDefaults,
31525           changeDefaults
31526         };
31527         });
31528
31529         /**
31530          * Helpers
31531          */
31532         const escapeTest = /[&<>"']/;
31533         const escapeReplace = /[&<>"']/g;
31534         const escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
31535         const escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
31536         const escapeReplacements = {
31537           '&': '&amp;',
31538           '<': '&lt;',
31539           '>': '&gt;',
31540           '"': '&quot;',
31541           "'": '&#39;'
31542         };
31543         const getEscapeReplacement = (ch) => escapeReplacements[ch];
31544         function escape$1(html, encode) {
31545           if (encode) {
31546             if (escapeTest.test(html)) {
31547               return html.replace(escapeReplace, getEscapeReplacement);
31548             }
31549           } else {
31550             if (escapeTestNoEncode.test(html)) {
31551               return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
31552             }
31553           }
31554
31555           return html;
31556         }
31557
31558         const unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;
31559
31560         function unescape$1(html) {
31561           // explicitly match decimal, hex, and named HTML entities
31562           return html.replace(unescapeTest, (_, n) => {
31563             n = n.toLowerCase();
31564             if (n === 'colon') return ':';
31565             if (n.charAt(0) === '#') {
31566               return n.charAt(1) === 'x'
31567                 ? String.fromCharCode(parseInt(n.substring(2), 16))
31568                 : String.fromCharCode(+n.substring(1));
31569             }
31570             return '';
31571           });
31572         }
31573
31574         const caret = /(^|[^\[])\^/g;
31575         function edit(regex, opt) {
31576           regex = regex.source || regex;
31577           opt = opt || '';
31578           const obj = {
31579             replace: (name, val) => {
31580               val = val.source || val;
31581               val = val.replace(caret, '$1');
31582               regex = regex.replace(name, val);
31583               return obj;
31584             },
31585             getRegex: () => {
31586               return new RegExp(regex, opt);
31587             }
31588           };
31589           return obj;
31590         }
31591
31592         const nonWordAndColonTest = /[^\w:]/g;
31593         const originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
31594         function cleanUrl(sanitize, base, href) {
31595           if (sanitize) {
31596             let prot;
31597             try {
31598               prot = decodeURIComponent(unescape$1(href))
31599                 .replace(nonWordAndColonTest, '')
31600                 .toLowerCase();
31601             } catch (e) {
31602               return null;
31603             }
31604             if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
31605               return null;
31606             }
31607           }
31608           if (base && !originIndependentUrl.test(href)) {
31609             href = resolveUrl(base, href);
31610           }
31611           try {
31612             href = encodeURI(href).replace(/%25/g, '%');
31613           } catch (e) {
31614             return null;
31615           }
31616           return href;
31617         }
31618
31619         const baseUrls = {};
31620         const justDomain = /^[^:]+:\/*[^/]*$/;
31621         const protocol = /^([^:]+:)[\s\S]*$/;
31622         const domain = /^([^:]+:\/*[^/]*)[\s\S]*$/;
31623
31624         function resolveUrl(base, href) {
31625           if (!baseUrls[' ' + base]) {
31626             // we can ignore everything in base after the last slash of its path component,
31627             // but we might need to add _that_
31628             // https://tools.ietf.org/html/rfc3986#section-3
31629             if (justDomain.test(base)) {
31630               baseUrls[' ' + base] = base + '/';
31631             } else {
31632               baseUrls[' ' + base] = rtrim(base, '/', true);
31633             }
31634           }
31635           base = baseUrls[' ' + base];
31636           const relativeBase = base.indexOf(':') === -1;
31637
31638           if (href.substring(0, 2) === '//') {
31639             if (relativeBase) {
31640               return href;
31641             }
31642             return base.replace(protocol, '$1') + href;
31643           } else if (href.charAt(0) === '/') {
31644             if (relativeBase) {
31645               return href;
31646             }
31647             return base.replace(domain, '$1') + href;
31648           } else {
31649             return base + href;
31650           }
31651         }
31652
31653         const noopTest = { exec: function noopTest() {} };
31654
31655         function merge$1(obj) {
31656           let i = 1,
31657             target,
31658             key;
31659
31660           for (; i < arguments.length; i++) {
31661             target = arguments[i];
31662             for (key in target) {
31663               if (Object.prototype.hasOwnProperty.call(target, key)) {
31664                 obj[key] = target[key];
31665               }
31666             }
31667           }
31668
31669           return obj;
31670         }
31671
31672         function splitCells(tableRow, count) {
31673           // ensure that every cell-delimiting pipe has a space
31674           // before it to distinguish it from an escaped pipe
31675           const row = tableRow.replace(/\|/g, (match, offset, str) => {
31676               let escaped = false,
31677                 curr = offset;
31678               while (--curr >= 0 && str[curr] === '\\') escaped = !escaped;
31679               if (escaped) {
31680                 // odd number of slashes means | is escaped
31681                 // so we leave it alone
31682                 return '|';
31683               } else {
31684                 // add space before unescaped |
31685                 return ' |';
31686               }
31687             }),
31688             cells = row.split(/ \|/);
31689           let i = 0;
31690
31691           if (cells.length > count) {
31692             cells.splice(count);
31693           } else {
31694             while (cells.length < count) cells.push('');
31695           }
31696
31697           for (; i < cells.length; i++) {
31698             // leading or trailing whitespace is ignored per the gfm spec
31699             cells[i] = cells[i].trim().replace(/\\\|/g, '|');
31700           }
31701           return cells;
31702         }
31703
31704         // Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
31705         // /c*$/ is vulnerable to REDOS.
31706         // invert: Remove suffix of non-c chars instead. Default falsey.
31707         function rtrim(str, c, invert) {
31708           const l = str.length;
31709           if (l === 0) {
31710             return '';
31711           }
31712
31713           // Length of suffix matching the invert condition.
31714           let suffLen = 0;
31715
31716           // Step left until we fail to match the invert condition.
31717           while (suffLen < l) {
31718             const currChar = str.charAt(l - suffLen - 1);
31719             if (currChar === c && !invert) {
31720               suffLen++;
31721             } else if (currChar !== c && invert) {
31722               suffLen++;
31723             } else {
31724               break;
31725             }
31726           }
31727
31728           return str.substr(0, l - suffLen);
31729         }
31730
31731         function findClosingBracket(str, b) {
31732           if (str.indexOf(b[1]) === -1) {
31733             return -1;
31734           }
31735           const l = str.length;
31736           let level = 0,
31737             i = 0;
31738           for (; i < l; i++) {
31739             if (str[i] === '\\') {
31740               i++;
31741             } else if (str[i] === b[0]) {
31742               level++;
31743             } else if (str[i] === b[1]) {
31744               level--;
31745               if (level < 0) {
31746                 return i;
31747               }
31748             }
31749           }
31750           return -1;
31751         }
31752
31753         function checkSanitizeDeprecation(opt) {
31754           if (opt && opt.sanitize && !opt.silent) {
31755             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');
31756           }
31757         }
31758
31759         var helpers = {
31760           escape: escape$1,
31761           unescape: unescape$1,
31762           edit,
31763           cleanUrl,
31764           resolveUrl,
31765           noopTest,
31766           merge: merge$1,
31767           splitCells,
31768           rtrim,
31769           findClosingBracket,
31770           checkSanitizeDeprecation
31771         };
31772
31773         const { defaults: defaults$1 } = defaults;
31774         const {
31775           rtrim: rtrim$1,
31776           splitCells: splitCells$1,
31777           escape: escape$2,
31778           findClosingBracket: findClosingBracket$1
31779         } = helpers;
31780
31781         function outputLink(cap, link, raw) {
31782           const href = link.href;
31783           const title = link.title ? escape$2(link.title) : null;
31784
31785           if (cap[0].charAt(0) !== '!') {
31786             return {
31787               type: 'link',
31788               raw,
31789               href,
31790               title,
31791               text: cap[1]
31792             };
31793           } else {
31794             return {
31795               type: 'image',
31796               raw,
31797               text: escape$2(cap[1]),
31798               href,
31799               title
31800             };
31801           }
31802         }
31803
31804         /**
31805          * Tokenizer
31806          */
31807         var Tokenizer_1 = class Tokenizer {
31808           constructor(options) {
31809             this.options = options || defaults$1;
31810           }
31811
31812           space(src) {
31813             const cap = this.rules.block.newline.exec(src);
31814             if (cap) {
31815               if (cap[0].length > 1) {
31816                 return {
31817                   type: 'space',
31818                   raw: cap[0]
31819                 };
31820               }
31821               return { raw: '\n' };
31822             }
31823           }
31824
31825           code(src, tokens) {
31826             const cap = this.rules.block.code.exec(src);
31827             if (cap) {
31828               const lastToken = tokens[tokens.length - 1];
31829               // An indented code block cannot interrupt a paragraph.
31830               if (lastToken && lastToken.type === 'paragraph') {
31831                 tokens.pop();
31832                 lastToken.text += '\n' + cap[0].trimRight();
31833                 lastToken.raw += '\n' + cap[0];
31834                 return lastToken;
31835               } else {
31836                 const text = cap[0].replace(/^ {4}/gm, '');
31837                 return {
31838                   type: 'code',
31839                   raw: cap[0],
31840                   codeBlockStyle: 'indented',
31841                   text: !this.options.pedantic
31842                     ? rtrim$1(text, '\n')
31843                     : text
31844                 };
31845               }
31846             }
31847           }
31848
31849           fences(src) {
31850             const cap = this.rules.block.fences.exec(src);
31851             if (cap) {
31852               return {
31853                 type: 'code',
31854                 raw: cap[0],
31855                 lang: cap[2] ? cap[2].trim() : cap[2],
31856                 text: cap[3] || ''
31857               };
31858             }
31859           }
31860
31861           heading(src) {
31862             const cap = this.rules.block.heading.exec(src);
31863             if (cap) {
31864               return {
31865                 type: 'heading',
31866                 raw: cap[0],
31867                 depth: cap[1].length,
31868                 text: cap[2]
31869               };
31870             }
31871           }
31872
31873           nptable(src) {
31874             const cap = this.rules.block.nptable.exec(src);
31875             if (cap) {
31876               const item = {
31877                 type: 'table',
31878                 header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
31879                 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
31880                 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [],
31881                 raw: cap[0]
31882               };
31883
31884               if (item.header.length === item.align.length) {
31885                 let l = item.align.length;
31886                 let i;
31887                 for (i = 0; i < l; i++) {
31888                   if (/^ *-+: *$/.test(item.align[i])) {
31889                     item.align[i] = 'right';
31890                   } else if (/^ *:-+: *$/.test(item.align[i])) {
31891                     item.align[i] = 'center';
31892                   } else if (/^ *:-+ *$/.test(item.align[i])) {
31893                     item.align[i] = 'left';
31894                   } else {
31895                     item.align[i] = null;
31896                   }
31897                 }
31898
31899                 l = item.cells.length;
31900                 for (i = 0; i < l; i++) {
31901                   item.cells[i] = splitCells$1(item.cells[i], item.header.length);
31902                 }
31903
31904                 return item;
31905               }
31906             }
31907           }
31908
31909           hr(src) {
31910             const cap = this.rules.block.hr.exec(src);
31911             if (cap) {
31912               return {
31913                 type: 'hr',
31914                 raw: cap[0]
31915               };
31916             }
31917           }
31918
31919           blockquote(src) {
31920             const cap = this.rules.block.blockquote.exec(src);
31921             if (cap) {
31922               const text = cap[0].replace(/^ *> ?/gm, '');
31923
31924               return {
31925                 type: 'blockquote',
31926                 raw: cap[0],
31927                 text
31928               };
31929             }
31930           }
31931
31932           list(src) {
31933             const cap = this.rules.block.list.exec(src);
31934             if (cap) {
31935               let raw = cap[0];
31936               const bull = cap[2];
31937               const isordered = bull.length > 1;
31938
31939               const list = {
31940                 type: 'list',
31941                 raw,
31942                 ordered: isordered,
31943                 start: isordered ? +bull : '',
31944                 loose: false,
31945                 items: []
31946               };
31947
31948               // Get each top-level item.
31949               const itemMatch = cap[0].match(this.rules.block.item);
31950
31951               let next = false,
31952                 item,
31953                 space,
31954                 b,
31955                 addBack,
31956                 loose,
31957                 istask,
31958                 ischecked;
31959
31960               const l = itemMatch.length;
31961               for (let i = 0; i < l; i++) {
31962                 item = itemMatch[i];
31963                 raw = item;
31964
31965                 // Remove the list item's bullet
31966                 // so it is seen as the next token.
31967                 space = item.length;
31968                 item = item.replace(/^ *([*+-]|\d+\.) */, '');
31969
31970                 // Outdent whatever the
31971                 // list item contains. Hacky.
31972                 if (~item.indexOf('\n ')) {
31973                   space -= item.length;
31974                   item = !this.options.pedantic
31975                     ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
31976                     : item.replace(/^ {1,4}/gm, '');
31977                 }
31978
31979                 // Determine whether the next list item belongs here.
31980                 // Backpedal if it does not belong in this list.
31981                 if (i !== l - 1) {
31982                   b = this.rules.block.bullet.exec(itemMatch[i + 1])[0];
31983                   if (bull.length > 1 ? b.length === 1
31984                     : (b.length > 1 || (this.options.smartLists && b !== bull))) {
31985                     addBack = itemMatch.slice(i + 1).join('\n');
31986                     list.raw = list.raw.substring(0, list.raw.length - addBack.length);
31987                     i = l - 1;
31988                   }
31989                 }
31990
31991                 // Determine whether item is loose or not.
31992                 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
31993                 // for discount behavior.
31994                 loose = next || /\n\n(?!\s*$)/.test(item);
31995                 if (i !== l - 1) {
31996                   next = item.charAt(item.length - 1) === '\n';
31997                   if (!loose) loose = next;
31998                 }
31999
32000                 if (loose) {
32001                   list.loose = true;
32002                 }
32003
32004                 // Check for task list items
32005                 istask = /^\[[ xX]\] /.test(item);
32006                 ischecked = undefined;
32007                 if (istask) {
32008                   ischecked = item[1] !== ' ';
32009                   item = item.replace(/^\[[ xX]\] +/, '');
32010                 }
32011
32012                 list.items.push({
32013                   raw,
32014                   task: istask,
32015                   checked: ischecked,
32016                   loose: loose,
32017                   text: item
32018                 });
32019               }
32020
32021               return list;
32022             }
32023           }
32024
32025           html(src) {
32026             const cap = this.rules.block.html.exec(src);
32027             if (cap) {
32028               return {
32029                 type: this.options.sanitize
32030                   ? 'paragraph'
32031                   : 'html',
32032                 raw: cap[0],
32033                 pre: !this.options.sanitizer
32034                   && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
32035                 text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$2(cap[0])) : cap[0]
32036               };
32037             }
32038           }
32039
32040           def(src) {
32041             const cap = this.rules.block.def.exec(src);
32042             if (cap) {
32043               if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
32044               const tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
32045               return {
32046                 tag,
32047                 raw: cap[0],
32048                 href: cap[2],
32049                 title: cap[3]
32050               };
32051             }
32052           }
32053
32054           table(src) {
32055             const cap = this.rules.block.table.exec(src);
32056             if (cap) {
32057               const item = {
32058                 type: 'table',
32059                 header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
32060                 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
32061                 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
32062               };
32063
32064               if (item.header.length === item.align.length) {
32065                 item.raw = cap[0];
32066
32067                 let l = item.align.length;
32068                 let i;
32069                 for (i = 0; i < l; i++) {
32070                   if (/^ *-+: *$/.test(item.align[i])) {
32071                     item.align[i] = 'right';
32072                   } else if (/^ *:-+: *$/.test(item.align[i])) {
32073                     item.align[i] = 'center';
32074                   } else if (/^ *:-+ *$/.test(item.align[i])) {
32075                     item.align[i] = 'left';
32076                   } else {
32077                     item.align[i] = null;
32078                   }
32079                 }
32080
32081                 l = item.cells.length;
32082                 for (i = 0; i < l; i++) {
32083                   item.cells[i] = splitCells$1(
32084                     item.cells[i].replace(/^ *\| *| *\| *$/g, ''),
32085                     item.header.length);
32086                 }
32087
32088                 return item;
32089               }
32090             }
32091           }
32092
32093           lheading(src) {
32094             const cap = this.rules.block.lheading.exec(src);
32095             if (cap) {
32096               return {
32097                 type: 'heading',
32098                 raw: cap[0],
32099                 depth: cap[2].charAt(0) === '=' ? 1 : 2,
32100                 text: cap[1]
32101               };
32102             }
32103           }
32104
32105           paragraph(src) {
32106             const cap = this.rules.block.paragraph.exec(src);
32107             if (cap) {
32108               return {
32109                 type: 'paragraph',
32110                 raw: cap[0],
32111                 text: cap[1].charAt(cap[1].length - 1) === '\n'
32112                   ? cap[1].slice(0, -1)
32113                   : cap[1]
32114               };
32115             }
32116           }
32117
32118           text(src) {
32119             const cap = this.rules.block.text.exec(src);
32120             if (cap) {
32121               return {
32122                 type: 'text',
32123                 raw: cap[0],
32124                 text: cap[0]
32125               };
32126             }
32127           }
32128
32129           escape(src) {
32130             const cap = this.rules.inline.escape.exec(src);
32131             if (cap) {
32132               return {
32133                 type: 'escape',
32134                 raw: cap[0],
32135                 text: escape$2(cap[1])
32136               };
32137             }
32138           }
32139
32140           tag(src, inLink, inRawBlock) {
32141             const cap = this.rules.inline.tag.exec(src);
32142             if (cap) {
32143               if (!inLink && /^<a /i.test(cap[0])) {
32144                 inLink = true;
32145               } else if (inLink && /^<\/a>/i.test(cap[0])) {
32146                 inLink = false;
32147               }
32148               if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
32149                 inRawBlock = true;
32150               } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
32151                 inRawBlock = false;
32152               }
32153
32154               return {
32155                 type: this.options.sanitize
32156                   ? 'text'
32157                   : 'html',
32158                 raw: cap[0],
32159                 inLink,
32160                 inRawBlock,
32161                 text: this.options.sanitize
32162                   ? (this.options.sanitizer
32163                     ? this.options.sanitizer(cap[0])
32164                     : escape$2(cap[0]))
32165                   : cap[0]
32166               };
32167             }
32168           }
32169
32170           link(src) {
32171             const cap = this.rules.inline.link.exec(src);
32172             if (cap) {
32173               const lastParenIndex = findClosingBracket$1(cap[2], '()');
32174               if (lastParenIndex > -1) {
32175                 const start = cap[0].indexOf('!') === 0 ? 5 : 4;
32176                 const linkLen = start + cap[1].length + lastParenIndex;
32177                 cap[2] = cap[2].substring(0, lastParenIndex);
32178                 cap[0] = cap[0].substring(0, linkLen).trim();
32179                 cap[3] = '';
32180               }
32181               let href = cap[2];
32182               let title = '';
32183               if (this.options.pedantic) {
32184                 const link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
32185
32186                 if (link) {
32187                   href = link[1];
32188                   title = link[3];
32189                 } else {
32190                   title = '';
32191                 }
32192               } else {
32193                 title = cap[3] ? cap[3].slice(1, -1) : '';
32194               }
32195               href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
32196               const token = outputLink(cap, {
32197                 href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
32198                 title: title ? title.replace(this.rules.inline._escapes, '$1') : title
32199               }, cap[0]);
32200               return token;
32201             }
32202           }
32203
32204           reflink(src, links) {
32205             let cap;
32206             if ((cap = this.rules.inline.reflink.exec(src))
32207                 || (cap = this.rules.inline.nolink.exec(src))) {
32208               let link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
32209               link = links[link.toLowerCase()];
32210               if (!link || !link.href) {
32211                 const text = cap[0].charAt(0);
32212                 return {
32213                   type: 'text',
32214                   raw: text,
32215                   text
32216                 };
32217               }
32218               const token = outputLink(cap, link, cap[0]);
32219               return token;
32220             }
32221           }
32222
32223           strong(src) {
32224             const cap = this.rules.inline.strong.exec(src);
32225             if (cap) {
32226               return {
32227                 type: 'strong',
32228                 raw: cap[0],
32229                 text: cap[4] || cap[3] || cap[2] || cap[1]
32230               };
32231             }
32232           }
32233
32234           em(src) {
32235             const cap = this.rules.inline.em.exec(src);
32236             if (cap) {
32237               return {
32238                 type: 'em',
32239                 raw: cap[0],
32240                 text: cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1]
32241               };
32242             }
32243           }
32244
32245           codespan(src) {
32246             const cap = this.rules.inline.code.exec(src);
32247             if (cap) {
32248               return {
32249                 type: 'codespan',
32250                 raw: cap[0],
32251                 text: escape$2(cap[2].trim(), true)
32252               };
32253             }
32254           }
32255
32256           br(src) {
32257             const cap = this.rules.inline.br.exec(src);
32258             if (cap) {
32259               return {
32260                 type: 'br',
32261                 raw: cap[0]
32262               };
32263             }
32264           }
32265
32266           del(src) {
32267             const cap = this.rules.inline.del.exec(src);
32268             if (cap) {
32269               return {
32270                 type: 'del',
32271                 raw: cap[0],
32272                 text: cap[1]
32273               };
32274             }
32275           }
32276
32277           autolink(src, mangle) {
32278             const cap = this.rules.inline.autolink.exec(src);
32279             if (cap) {
32280               let text, href;
32281               if (cap[2] === '@') {
32282                 text = escape$2(this.options.mangle ? mangle(cap[1]) : cap[1]);
32283                 href = 'mailto:' + text;
32284               } else {
32285                 text = escape$2(cap[1]);
32286                 href = text;
32287               }
32288
32289               return {
32290                 type: 'link',
32291                 raw: cap[0],
32292                 text,
32293                 href,
32294                 tokens: [
32295                   {
32296                     type: 'text',
32297                     raw: text,
32298                     text
32299                   }
32300                 ]
32301               };
32302             }
32303           }
32304
32305           url(src, mangle) {
32306             let cap;
32307             if (cap = this.rules.inline.url.exec(src)) {
32308               let text, href;
32309               if (cap[2] === '@') {
32310                 text = escape$2(this.options.mangle ? mangle(cap[0]) : cap[0]);
32311                 href = 'mailto:' + text;
32312               } else {
32313                 // do extended autolink path validation
32314                 let prevCapZero;
32315                 do {
32316                   prevCapZero = cap[0];
32317                   cap[0] = this.rules.inline._backpedal.exec(cap[0])[0];
32318                 } while (prevCapZero !== cap[0]);
32319                 text = escape$2(cap[0]);
32320                 if (cap[1] === 'www.') {
32321                   href = 'http://' + text;
32322                 } else {
32323                   href = text;
32324                 }
32325               }
32326               return {
32327                 type: 'link',
32328                 raw: cap[0],
32329                 text,
32330                 href,
32331                 tokens: [
32332                   {
32333                     type: 'text',
32334                     raw: text,
32335                     text
32336                   }
32337                 ]
32338               };
32339             }
32340           }
32341
32342           inlineText(src, inRawBlock, smartypants) {
32343             const cap = this.rules.inline.text.exec(src);
32344             if (cap) {
32345               let text;
32346               if (inRawBlock) {
32347                 text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$2(cap[0])) : cap[0];
32348               } else {
32349                 text = escape$2(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
32350               }
32351               return {
32352                 type: 'text',
32353                 raw: cap[0],
32354                 text
32355               };
32356             }
32357           }
32358         };
32359
32360         const {
32361           noopTest: noopTest$1,
32362           edit: edit$1,
32363           merge: merge$2
32364         } = helpers;
32365
32366         /**
32367          * Block-Level Grammar
32368          */
32369         const block = {
32370           newline: /^\n+/,
32371           code: /^( {4}[^\n]+\n*)+/,
32372           fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,
32373           hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
32374           heading: /^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,
32375           blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
32376           list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
32377           html: '^ {0,3}(?:' // optional indentation
32378             + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
32379             + '|comment[^\\n]*(\\n+|$)' // (2)
32380             + '|<\\?[\\s\\S]*?\\?>\\n*' // (3)
32381             + '|<![A-Z][\\s\\S]*?>\\n*' // (4)
32382             + '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>\\n*' // (5)
32383             + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)' // (6)
32384             + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
32385             + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag
32386             + ')',
32387           def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
32388           nptable: noopTest$1,
32389           table: noopTest$1,
32390           lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,
32391           // regex template, placeholders will be replaced according to different paragraph
32392           // interruption rules of commonmark and the original markdown spec:
32393           _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,
32394           text: /^[^\n]+/
32395         };
32396
32397         block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
32398         block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
32399         block.def = edit$1(block.def)
32400           .replace('label', block._label)
32401           .replace('title', block._title)
32402           .getRegex();
32403
32404         block.bullet = /(?:[*+-]|\d{1,9}\.)/;
32405         block.item = /^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/;
32406         block.item = edit$1(block.item, 'gm')
32407           .replace(/bull/g, block.bullet)
32408           .getRegex();
32409
32410         block.list = edit$1(block.list)
32411           .replace(/bull/g, block.bullet)
32412           .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))')
32413           .replace('def', '\\n+(?=' + block.def.source + ')')
32414           .getRegex();
32415
32416         block._tag = 'address|article|aside|base|basefont|blockquote|body|caption'
32417           + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'
32418           + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'
32419           + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'
32420           + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr'
32421           + '|track|ul';
32422         block._comment = /<!--(?!-?>)[\s\S]*?-->/;
32423         block.html = edit$1(block.html, 'i')
32424           .replace('comment', block._comment)
32425           .replace('tag', block._tag)
32426           .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/)
32427           .getRegex();
32428
32429         block.paragraph = edit$1(block._paragraph)
32430           .replace('hr', block.hr)
32431           .replace('heading', ' {0,3}#{1,6} ')
32432           .replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs
32433           .replace('blockquote', ' {0,3}>')
32434           .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
32435           .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
32436           .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)')
32437           .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
32438           .getRegex();
32439
32440         block.blockquote = edit$1(block.blockquote)
32441           .replace('paragraph', block.paragraph)
32442           .getRegex();
32443
32444         /**
32445          * Normal Block Grammar
32446          */
32447
32448         block.normal = merge$2({}, block);
32449
32450         /**
32451          * GFM Block Grammar
32452          */
32453
32454         block.gfm = merge$2({}, block.normal, {
32455           nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header
32456             + ' *([-:]+ *\\|[-| :]*)' // Align
32457             + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', // Cells
32458           table: '^ *\\|(.+)\\n' // Header
32459             + ' *\\|?( *[-:]+[-| :]*)' // Align
32460             + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells
32461         });
32462
32463         block.gfm.nptable = edit$1(block.gfm.nptable)
32464           .replace('hr', block.hr)
32465           .replace('heading', ' {0,3}#{1,6} ')
32466           .replace('blockquote', ' {0,3}>')
32467           .replace('code', ' {4}[^\\n]')
32468           .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
32469           .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
32470           .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)')
32471           .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
32472           .getRegex();
32473
32474         block.gfm.table = edit$1(block.gfm.table)
32475           .replace('hr', block.hr)
32476           .replace('heading', ' {0,3}#{1,6} ')
32477           .replace('blockquote', ' {0,3}>')
32478           .replace('code', ' {4}[^\\n]')
32479           .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
32480           .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
32481           .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)')
32482           .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
32483           .getRegex();
32484
32485         /**
32486          * Pedantic grammar (original John Gruber's loose markdown specification)
32487          */
32488
32489         block.pedantic = merge$2({}, block.normal, {
32490           html: edit$1(
32491             '^ *(?:comment *(?:\\n|\\s*$)'
32492             + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
32493             + '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))')
32494             .replace('comment', block._comment)
32495             .replace(/tag/g, '(?!(?:'
32496               + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'
32497               + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'
32498               + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b')
32499             .getRegex(),
32500           def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
32501           heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,
32502           fences: noopTest$1, // fences not supported
32503           paragraph: edit$1(block.normal._paragraph)
32504             .replace('hr', block.hr)
32505             .replace('heading', ' *#{1,6} *[^\n]')
32506             .replace('lheading', block.lheading)
32507             .replace('blockquote', ' {0,3}>')
32508             .replace('|fences', '')
32509             .replace('|list', '')
32510             .replace('|html', '')
32511             .getRegex()
32512         });
32513
32514         /**
32515          * Inline-Level Grammar
32516          */
32517         const inline = {
32518           escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
32519           autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
32520           url: noopTest$1,
32521           tag: '^comment'
32522             + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
32523             + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
32524             + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
32525             + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
32526             + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>', // CDATA section
32527           link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,
32528           reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
32529           nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
32530           strong: /^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,
32531           em: /^_([^\s_])_(?!_)|^_([^\s_<][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_<][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s*<\[])\*(?!\*)|^\*([^\s<"][\s\S]*?[^\s\[\*])\*(?![\]`punctuation])|^\*([^\s*"<\[][\s\S]*[^\s])\*(?!\*)/,
32532           code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
32533           br: /^( {2,}|\\)\n(?!\s*$)/,
32534           del: noopTest$1,
32535           text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\<!\[`*]|\b_|$)|[^ ](?= {2,}\n))|(?= {2,}\n))/
32536         };
32537
32538         // list of punctuation marks from common mark spec
32539         // without ` and ] to workaround Rule 17 (inline code blocks/links)
32540         inline._punctuation = '!"#$%&\'()*+\\-./:;<=>?@\\[^_{|}~';
32541         inline.em = edit$1(inline.em).replace(/punctuation/g, inline._punctuation).getRegex();
32542
32543         inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
32544
32545         inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
32546         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])?)+(?![-_])/;
32547         inline.autolink = edit$1(inline.autolink)
32548           .replace('scheme', inline._scheme)
32549           .replace('email', inline._email)
32550           .getRegex();
32551
32552         inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
32553
32554         inline.tag = edit$1(inline.tag)
32555           .replace('comment', block._comment)
32556           .replace('attribute', inline._attribute)
32557           .getRegex();
32558
32559         inline._label = /(?:\[[^\[\]]*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
32560         inline._href = /<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/;
32561         inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
32562
32563         inline.link = edit$1(inline.link)
32564           .replace('label', inline._label)
32565           .replace('href', inline._href)
32566           .replace('title', inline._title)
32567           .getRegex();
32568
32569         inline.reflink = edit$1(inline.reflink)
32570           .replace('label', inline._label)
32571           .getRegex();
32572
32573         /**
32574          * Normal Inline Grammar
32575          */
32576
32577         inline.normal = merge$2({}, inline);
32578
32579         /**
32580          * Pedantic Inline Grammar
32581          */
32582
32583         inline.pedantic = merge$2({}, inline.normal, {
32584           strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
32585           em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,
32586           link: edit$1(/^!?\[(label)\]\((.*?)\)/)
32587             .replace('label', inline._label)
32588             .getRegex(),
32589           reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/)
32590             .replace('label', inline._label)
32591             .getRegex()
32592         });
32593
32594         /**
32595          * GFM Inline Grammar
32596          */
32597
32598         inline.gfm = merge$2({}, inline.normal, {
32599           escape: edit$1(inline.escape).replace('])', '~|])').getRegex(),
32600           _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
32601           url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
32602           _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
32603           del: /^~+(?=\S)([\s\S]*?\S)~+/,
32604           text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\<!\[`*~]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))|(?= {2,}\n|[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))/
32605         });
32606
32607         inline.gfm.url = edit$1(inline.gfm.url, 'i')
32608           .replace('email', inline.gfm._extended_email)
32609           .getRegex();
32610         /**
32611          * GFM + Line Breaks Inline Grammar
32612          */
32613
32614         inline.breaks = merge$2({}, inline.gfm, {
32615           br: edit$1(inline.br).replace('{2,}', '*').getRegex(),
32616           text: edit$1(inline.gfm.text)
32617             .replace('\\b_', '\\b_| {2,}\\n')
32618             .replace(/\{2,\}/g, '*')
32619             .getRegex()
32620         });
32621
32622         var rules = {
32623           block,
32624           inline
32625         };
32626
32627         const { defaults: defaults$2 } = defaults;
32628         const { block: block$1, inline: inline$1 } = rules;
32629
32630         /**
32631          * smartypants text replacement
32632          */
32633         function smartypants(text) {
32634           return text
32635             // em-dashes
32636             .replace(/---/g, '\u2014')
32637             // en-dashes
32638             .replace(/--/g, '\u2013')
32639             // opening singles
32640             .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
32641             // closing singles & apostrophes
32642             .replace(/'/g, '\u2019')
32643             // opening doubles
32644             .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
32645             // closing doubles
32646             .replace(/"/g, '\u201d')
32647             // ellipses
32648             .replace(/\.{3}/g, '\u2026');
32649         }
32650
32651         /**
32652          * mangle email addresses
32653          */
32654         function mangle(text) {
32655           let out = '',
32656             i,
32657             ch;
32658
32659           const l = text.length;
32660           for (i = 0; i < l; i++) {
32661             ch = text.charCodeAt(i);
32662             if (Math.random() > 0.5) {
32663               ch = 'x' + ch.toString(16);
32664             }
32665             out += '&#' + ch + ';';
32666           }
32667
32668           return out;
32669         }
32670
32671         /**
32672          * Block Lexer
32673          */
32674         var Lexer_1 = class Lexer {
32675           constructor(options) {
32676             this.tokens = [];
32677             this.tokens.links = Object.create(null);
32678             this.options = options || defaults$2;
32679             this.options.tokenizer = this.options.tokenizer || new Tokenizer_1();
32680             this.tokenizer = this.options.tokenizer;
32681             this.tokenizer.options = this.options;
32682
32683             const rules = {
32684               block: block$1.normal,
32685               inline: inline$1.normal
32686             };
32687
32688             if (this.options.pedantic) {
32689               rules.block = block$1.pedantic;
32690               rules.inline = inline$1.pedantic;
32691             } else if (this.options.gfm) {
32692               rules.block = block$1.gfm;
32693               if (this.options.breaks) {
32694                 rules.inline = inline$1.breaks;
32695               } else {
32696                 rules.inline = inline$1.gfm;
32697               }
32698             }
32699             this.tokenizer.rules = rules;
32700           }
32701
32702           /**
32703            * Expose Rules
32704            */
32705           static get rules() {
32706             return {
32707               block: block$1,
32708               inline: inline$1
32709             };
32710           }
32711
32712           /**
32713            * Static Lex Method
32714            */
32715           static lex(src, options) {
32716             const lexer = new Lexer(options);
32717             return lexer.lex(src);
32718           }
32719
32720           /**
32721            * Preprocessing
32722            */
32723           lex(src) {
32724             src = src
32725               .replace(/\r\n|\r/g, '\n')
32726               .replace(/\t/g, '    ');
32727
32728             this.blockTokens(src, this.tokens, true);
32729
32730             this.inline(this.tokens);
32731
32732             return this.tokens;
32733           }
32734
32735           /**
32736            * Lexing
32737            */
32738           blockTokens(src, tokens = [], top = true) {
32739             src = src.replace(/^ +$/gm, '');
32740             let token, i, l;
32741
32742             while (src) {
32743               // newline
32744               if (token = this.tokenizer.space(src)) {
32745                 src = src.substring(token.raw.length);
32746                 if (token.type) {
32747                   tokens.push(token);
32748                 }
32749                 continue;
32750               }
32751
32752               // code
32753               if (token = this.tokenizer.code(src, tokens)) {
32754                 src = src.substring(token.raw.length);
32755                 tokens.push(token);
32756                 continue;
32757               }
32758
32759               // fences
32760               if (token = this.tokenizer.fences(src)) {
32761                 src = src.substring(token.raw.length);
32762                 tokens.push(token);
32763                 continue;
32764               }
32765
32766               // heading
32767               if (token = this.tokenizer.heading(src)) {
32768                 src = src.substring(token.raw.length);
32769                 tokens.push(token);
32770                 continue;
32771               }
32772
32773               // table no leading pipe (gfm)
32774               if (token = this.tokenizer.nptable(src)) {
32775                 src = src.substring(token.raw.length);
32776                 tokens.push(token);
32777                 continue;
32778               }
32779
32780               // hr
32781               if (token = this.tokenizer.hr(src)) {
32782                 src = src.substring(token.raw.length);
32783                 tokens.push(token);
32784                 continue;
32785               }
32786
32787               // blockquote
32788               if (token = this.tokenizer.blockquote(src)) {
32789                 src = src.substring(token.raw.length);
32790                 token.tokens = this.blockTokens(token.text, [], top);
32791                 tokens.push(token);
32792                 continue;
32793               }
32794
32795               // list
32796               if (token = this.tokenizer.list(src)) {
32797                 src = src.substring(token.raw.length);
32798                 l = token.items.length;
32799                 for (i = 0; i < l; i++) {
32800                   token.items[i].tokens = this.blockTokens(token.items[i].text, [], false);
32801                 }
32802                 tokens.push(token);
32803                 continue;
32804               }
32805
32806               // html
32807               if (token = this.tokenizer.html(src)) {
32808                 src = src.substring(token.raw.length);
32809                 tokens.push(token);
32810                 continue;
32811               }
32812
32813               // def
32814               if (top && (token = this.tokenizer.def(src))) {
32815                 src = src.substring(token.raw.length);
32816                 if (!this.tokens.links[token.tag]) {
32817                   this.tokens.links[token.tag] = {
32818                     href: token.href,
32819                     title: token.title
32820                   };
32821                 }
32822                 continue;
32823               }
32824
32825               // table (gfm)
32826               if (token = this.tokenizer.table(src)) {
32827                 src = src.substring(token.raw.length);
32828                 tokens.push(token);
32829                 continue;
32830               }
32831
32832               // lheading
32833               if (token = this.tokenizer.lheading(src)) {
32834                 src = src.substring(token.raw.length);
32835                 tokens.push(token);
32836                 continue;
32837               }
32838
32839               // top-level paragraph
32840               if (top && (token = this.tokenizer.paragraph(src))) {
32841                 src = src.substring(token.raw.length);
32842                 tokens.push(token);
32843                 continue;
32844               }
32845
32846               // text
32847               if (token = this.tokenizer.text(src)) {
32848                 src = src.substring(token.raw.length);
32849                 tokens.push(token);
32850                 continue;
32851               }
32852
32853               if (src) {
32854                 const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
32855                 if (this.options.silent) {
32856                   console.error(errMsg);
32857                   break;
32858                 } else {
32859                   throw new Error(errMsg);
32860                 }
32861               }
32862             }
32863
32864             return tokens;
32865           }
32866
32867           inline(tokens) {
32868             let i,
32869               j,
32870               k,
32871               l2,
32872               row,
32873               token;
32874
32875             const l = tokens.length;
32876             for (i = 0; i < l; i++) {
32877               token = tokens[i];
32878               switch (token.type) {
32879                 case 'paragraph':
32880                 case 'text':
32881                 case 'heading': {
32882                   token.tokens = [];
32883                   this.inlineTokens(token.text, token.tokens);
32884                   break;
32885                 }
32886                 case 'table': {
32887                   token.tokens = {
32888                     header: [],
32889                     cells: []
32890                   };
32891
32892                   // header
32893                   l2 = token.header.length;
32894                   for (j = 0; j < l2; j++) {
32895                     token.tokens.header[j] = [];
32896                     this.inlineTokens(token.header[j], token.tokens.header[j]);
32897                   }
32898
32899                   // cells
32900                   l2 = token.cells.length;
32901                   for (j = 0; j < l2; j++) {
32902                     row = token.cells[j];
32903                     token.tokens.cells[j] = [];
32904                     for (k = 0; k < row.length; k++) {
32905                       token.tokens.cells[j][k] = [];
32906                       this.inlineTokens(row[k], token.tokens.cells[j][k]);
32907                     }
32908                   }
32909
32910                   break;
32911                 }
32912                 case 'blockquote': {
32913                   this.inline(token.tokens);
32914                   break;
32915                 }
32916                 case 'list': {
32917                   l2 = token.items.length;
32918                   for (j = 0; j < l2; j++) {
32919                     this.inline(token.items[j].tokens);
32920                   }
32921                   break;
32922                 }
32923               }
32924             }
32925
32926             return tokens;
32927           }
32928
32929           /**
32930            * Lexing/Compiling
32931            */
32932           inlineTokens(src, tokens = [], inLink = false, inRawBlock = false) {
32933             let token;
32934
32935             while (src) {
32936               // escape
32937               if (token = this.tokenizer.escape(src)) {
32938                 src = src.substring(token.raw.length);
32939                 tokens.push(token);
32940                 continue;
32941               }
32942
32943               // tag
32944               if (token = this.tokenizer.tag(src, inLink, inRawBlock)) {
32945                 src = src.substring(token.raw.length);
32946                 inLink = token.inLink;
32947                 inRawBlock = token.inRawBlock;
32948                 tokens.push(token);
32949                 continue;
32950               }
32951
32952               // link
32953               if (token = this.tokenizer.link(src)) {
32954                 src = src.substring(token.raw.length);
32955                 if (token.type === 'link') {
32956                   token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
32957                 }
32958                 tokens.push(token);
32959                 continue;
32960               }
32961
32962               // reflink, nolink
32963               if (token = this.tokenizer.reflink(src, this.tokens.links)) {
32964                 src = src.substring(token.raw.length);
32965                 if (token.type === 'link') {
32966                   token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
32967                 }
32968                 tokens.push(token);
32969                 continue;
32970               }
32971
32972               // strong
32973               if (token = this.tokenizer.strong(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               // em
32981               if (token = this.tokenizer.em(src)) {
32982                 src = src.substring(token.raw.length);
32983                 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
32984                 tokens.push(token);
32985                 continue;
32986               }
32987
32988               // code
32989               if (token = this.tokenizer.codespan(src)) {
32990                 src = src.substring(token.raw.length);
32991                 tokens.push(token);
32992                 continue;
32993               }
32994
32995               // br
32996               if (token = this.tokenizer.br(src)) {
32997                 src = src.substring(token.raw.length);
32998                 tokens.push(token);
32999                 continue;
33000               }
33001
33002               // del (gfm)
33003               if (token = this.tokenizer.del(src)) {
33004                 src = src.substring(token.raw.length);
33005                 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
33006                 tokens.push(token);
33007                 continue;
33008               }
33009
33010               // autolink
33011               if (token = this.tokenizer.autolink(src, mangle)) {
33012                 src = src.substring(token.raw.length);
33013                 tokens.push(token);
33014                 continue;
33015               }
33016
33017               // url (gfm)
33018               if (!inLink && (token = this.tokenizer.url(src, mangle))) {
33019                 src = src.substring(token.raw.length);
33020                 tokens.push(token);
33021                 continue;
33022               }
33023
33024               // text
33025               if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) {
33026                 src = src.substring(token.raw.length);
33027                 tokens.push(token);
33028                 continue;
33029               }
33030
33031               if (src) {
33032                 const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
33033                 if (this.options.silent) {
33034                   console.error(errMsg);
33035                   break;
33036                 } else {
33037                   throw new Error(errMsg);
33038                 }
33039               }
33040             }
33041
33042             return tokens;
33043           }
33044         };
33045
33046         const { defaults: defaults$3 } = defaults;
33047         const {
33048           cleanUrl: cleanUrl$1,
33049           escape: escape$3
33050         } = helpers;
33051
33052         /**
33053          * Renderer
33054          */
33055         var Renderer_1 = class Renderer {
33056           constructor(options) {
33057             this.options = options || defaults$3;
33058           }
33059
33060           code(code, infostring, escaped) {
33061             const lang = (infostring || '').match(/\S*/)[0];
33062             if (this.options.highlight) {
33063               const out = this.options.highlight(code, lang);
33064               if (out != null && out !== code) {
33065                 escaped = true;
33066                 code = out;
33067               }
33068             }
33069
33070             if (!lang) {
33071               return '<pre><code>'
33072                 + (escaped ? code : escape$3(code, true))
33073                 + '</code></pre>';
33074             }
33075
33076             return '<pre><code class="'
33077               + this.options.langPrefix
33078               + escape$3(lang, true)
33079               + '">'
33080               + (escaped ? code : escape$3(code, true))
33081               + '</code></pre>\n';
33082           }
33083
33084           blockquote(quote) {
33085             return '<blockquote>\n' + quote + '</blockquote>\n';
33086           }
33087
33088           html(html) {
33089             return html;
33090           }
33091
33092           heading(text, level, raw, slugger) {
33093             if (this.options.headerIds) {
33094               return '<h'
33095                 + level
33096                 + ' id="'
33097                 + this.options.headerPrefix
33098                 + slugger.slug(raw)
33099                 + '">'
33100                 + text
33101                 + '</h'
33102                 + level
33103                 + '>\n';
33104             }
33105             // ignore IDs
33106             return '<h' + level + '>' + text + '</h' + level + '>\n';
33107           }
33108
33109           hr() {
33110             return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
33111           }
33112
33113           list(body, ordered, start) {
33114             const type = ordered ? 'ol' : 'ul',
33115               startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
33116             return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
33117           }
33118
33119           listitem(text) {
33120             return '<li>' + text + '</li>\n';
33121           }
33122
33123           checkbox(checked) {
33124             return '<input '
33125               + (checked ? 'checked="" ' : '')
33126               + 'disabled="" type="checkbox"'
33127               + (this.options.xhtml ? ' /' : '')
33128               + '> ';
33129           }
33130
33131           paragraph(text) {
33132             return '<p>' + text + '</p>\n';
33133           }
33134
33135           table(header, body) {
33136             if (body) body = '<tbody>' + body + '</tbody>';
33137
33138             return '<table>\n'
33139               + '<thead>\n'
33140               + header
33141               + '</thead>\n'
33142               + body
33143               + '</table>\n';
33144           }
33145
33146           tablerow(content) {
33147             return '<tr>\n' + content + '</tr>\n';
33148           }
33149
33150           tablecell(content, flags) {
33151             const type = flags.header ? 'th' : 'td';
33152             const tag = flags.align
33153               ? '<' + type + ' align="' + flags.align + '">'
33154               : '<' + type + '>';
33155             return tag + content + '</' + type + '>\n';
33156           }
33157
33158           // span level renderer
33159           strong(text) {
33160             return '<strong>' + text + '</strong>';
33161           }
33162
33163           em(text) {
33164             return '<em>' + text + '</em>';
33165           }
33166
33167           codespan(text) {
33168             return '<code>' + text + '</code>';
33169           }
33170
33171           br() {
33172             return this.options.xhtml ? '<br/>' : '<br>';
33173           }
33174
33175           del(text) {
33176             return '<del>' + text + '</del>';
33177           }
33178
33179           link(href, title, text) {
33180             href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);
33181             if (href === null) {
33182               return text;
33183             }
33184             let out = '<a href="' + escape$3(href) + '"';
33185             if (title) {
33186               out += ' title="' + title + '"';
33187             }
33188             out += '>' + text + '</a>';
33189             return out;
33190           }
33191
33192           image(href, title, text) {
33193             href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);
33194             if (href === null) {
33195               return text;
33196             }
33197
33198             let out = '<img src="' + href + '" alt="' + text + '"';
33199             if (title) {
33200               out += ' title="' + title + '"';
33201             }
33202             out += this.options.xhtml ? '/>' : '>';
33203             return out;
33204           }
33205
33206           text(text) {
33207             return text;
33208           }
33209         };
33210
33211         /**
33212          * TextRenderer
33213          * returns only the textual part of the token
33214          */
33215         var TextRenderer_1 = class TextRenderer {
33216           // no need for block level renderers
33217           strong(text) {
33218             return text;
33219           }
33220
33221           em(text) {
33222             return text;
33223           }
33224
33225           codespan(text) {
33226             return text;
33227           }
33228
33229           del(text) {
33230             return text;
33231           }
33232
33233           html(text) {
33234             return text;
33235           }
33236
33237           text(text) {
33238             return text;
33239           }
33240
33241           link(href, title, text) {
33242             return '' + text;
33243           }
33244
33245           image(href, title, text) {
33246             return '' + text;
33247           }
33248
33249           br() {
33250             return '';
33251           }
33252         };
33253
33254         /**
33255          * Slugger generates header id
33256          */
33257         var Slugger_1 = class Slugger {
33258           constructor() {
33259             this.seen = {};
33260           }
33261
33262           /**
33263            * Convert string to unique id
33264            */
33265           slug(value) {
33266             let slug = value
33267               .toLowerCase()
33268               .trim()
33269               // remove html tags
33270               .replace(/<[!\/a-z].*?>/ig, '')
33271               // remove unwanted chars
33272               .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '')
33273               .replace(/\s/g, '-');
33274
33275             if (this.seen.hasOwnProperty(slug)) {
33276               const originalSlug = slug;
33277               do {
33278                 this.seen[originalSlug]++;
33279                 slug = originalSlug + '-' + this.seen[originalSlug];
33280               } while (this.seen.hasOwnProperty(slug));
33281             }
33282             this.seen[slug] = 0;
33283
33284             return slug;
33285           }
33286         };
33287
33288         const { defaults: defaults$4 } = defaults;
33289         const {
33290           unescape: unescape$2
33291         } = helpers;
33292
33293         /**
33294          * Parsing & Compiling
33295          */
33296         var Parser_1 = class Parser {
33297           constructor(options) {
33298             this.options = options || defaults$4;
33299             this.options.renderer = this.options.renderer || new Renderer_1();
33300             this.renderer = this.options.renderer;
33301             this.renderer.options = this.options;
33302             this.textRenderer = new TextRenderer_1();
33303             this.slugger = new Slugger_1();
33304           }
33305
33306           /**
33307            * Static Parse Method
33308            */
33309           static parse(tokens, options) {
33310             const parser = new Parser(options);
33311             return parser.parse(tokens);
33312           }
33313
33314           /**
33315            * Parse Loop
33316            */
33317           parse(tokens, top = true) {
33318             let out = '',
33319               i,
33320               j,
33321               k,
33322               l2,
33323               l3,
33324               row,
33325               cell,
33326               header,
33327               body,
33328               token,
33329               ordered,
33330               start,
33331               loose,
33332               itemBody,
33333               item,
33334               checked,
33335               task,
33336               checkbox;
33337
33338             const l = tokens.length;
33339             for (i = 0; i < l; i++) {
33340               token = tokens[i];
33341               switch (token.type) {
33342                 case 'space': {
33343                   continue;
33344                 }
33345                 case 'hr': {
33346                   out += this.renderer.hr();
33347                   continue;
33348                 }
33349                 case 'heading': {
33350                   out += this.renderer.heading(
33351                     this.parseInline(token.tokens),
33352                     token.depth,
33353                     unescape$2(this.parseInline(token.tokens, this.textRenderer)),
33354                     this.slugger);
33355                   continue;
33356                 }
33357                 case 'code': {
33358                   out += this.renderer.code(token.text,
33359                     token.lang,
33360                     token.escaped);
33361                   continue;
33362                 }
33363                 case 'table': {
33364                   header = '';
33365
33366                   // header
33367                   cell = '';
33368                   l2 = token.header.length;
33369                   for (j = 0; j < l2; j++) {
33370                     cell += this.renderer.tablecell(
33371                       this.parseInline(token.tokens.header[j]),
33372                       { header: true, align: token.align[j] }
33373                     );
33374                   }
33375                   header += this.renderer.tablerow(cell);
33376
33377                   body = '';
33378                   l2 = token.cells.length;
33379                   for (j = 0; j < l2; j++) {
33380                     row = token.tokens.cells[j];
33381
33382                     cell = '';
33383                     l3 = row.length;
33384                     for (k = 0; k < l3; k++) {
33385                       cell += this.renderer.tablecell(
33386                         this.parseInline(row[k]),
33387                         { header: false, align: token.align[k] }
33388                       );
33389                     }
33390
33391                     body += this.renderer.tablerow(cell);
33392                   }
33393                   out += this.renderer.table(header, body);
33394                   continue;
33395                 }
33396                 case 'blockquote': {
33397                   body = this.parse(token.tokens);
33398                   out += this.renderer.blockquote(body);
33399                   continue;
33400                 }
33401                 case 'list': {
33402                   ordered = token.ordered;
33403                   start = token.start;
33404                   loose = token.loose;
33405                   l2 = token.items.length;
33406
33407                   body = '';
33408                   for (j = 0; j < l2; j++) {
33409                     item = token.items[j];
33410                     checked = item.checked;
33411                     task = item.task;
33412
33413                     itemBody = '';
33414                     if (item.task) {
33415                       checkbox = this.renderer.checkbox(checked);
33416                       if (loose) {
33417                         if (item.tokens[0].type === 'text') {
33418                           item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
33419                           if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
33420                             item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
33421                           }
33422                         } else {
33423                           item.tokens.unshift({
33424                             type: 'text',
33425                             text: checkbox
33426                           });
33427                         }
33428                       } else {
33429                         itemBody += checkbox;
33430                       }
33431                     }
33432
33433                     itemBody += this.parse(item.tokens, loose);
33434                     body += this.renderer.listitem(itemBody, task, checked);
33435                   }
33436
33437                   out += this.renderer.list(body, ordered, start);
33438                   continue;
33439                 }
33440                 case 'html': {
33441                   // TODO parse inline content if parameter markdown=1
33442                   out += this.renderer.html(token.text);
33443                   continue;
33444                 }
33445                 case 'paragraph': {
33446                   out += this.renderer.paragraph(this.parseInline(token.tokens));
33447                   continue;
33448                 }
33449                 case 'text': {
33450                   body = token.tokens ? this.parseInline(token.tokens) : token.text;
33451                   while (i + 1 < l && tokens[i + 1].type === 'text') {
33452                     token = tokens[++i];
33453                     body += '\n' + (token.tokens ? this.parseInline(token.tokens) : token.text);
33454                   }
33455                   out += top ? this.renderer.paragraph(body) : body;
33456                   continue;
33457                 }
33458                 default: {
33459                   const errMsg = 'Token with "' + token.type + '" type was not found.';
33460                   if (this.options.silent) {
33461                     console.error(errMsg);
33462                     return;
33463                   } else {
33464                     throw new Error(errMsg);
33465                   }
33466                 }
33467               }
33468             }
33469
33470             return out;
33471           }
33472
33473           /**
33474            * Parse Inline Tokens
33475            */
33476           parseInline(tokens, renderer) {
33477             renderer = renderer || this.renderer;
33478             let out = '',
33479               i,
33480               token;
33481
33482             const l = tokens.length;
33483             for (i = 0; i < l; i++) {
33484               token = tokens[i];
33485               switch (token.type) {
33486                 case 'escape': {
33487                   out += renderer.text(token.text);
33488                   break;
33489                 }
33490                 case 'html': {
33491                   out += renderer.html(token.text);
33492                   break;
33493                 }
33494                 case 'link': {
33495                   out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer));
33496                   break;
33497                 }
33498                 case 'image': {
33499                   out += renderer.image(token.href, token.title, token.text);
33500                   break;
33501                 }
33502                 case 'strong': {
33503                   out += renderer.strong(this.parseInline(token.tokens, renderer));
33504                   break;
33505                 }
33506                 case 'em': {
33507                   out += renderer.em(this.parseInline(token.tokens, renderer));
33508                   break;
33509                 }
33510                 case 'codespan': {
33511                   out += renderer.codespan(token.text);
33512                   break;
33513                 }
33514                 case 'br': {
33515                   out += renderer.br();
33516                   break;
33517                 }
33518                 case 'del': {
33519                   out += renderer.del(this.parseInline(token.tokens, renderer));
33520                   break;
33521                 }
33522                 case 'text': {
33523                   out += renderer.text(token.text);
33524                   break;
33525                 }
33526                 default: {
33527                   const errMsg = 'Token with "' + token.type + '" type was not found.';
33528                   if (this.options.silent) {
33529                     console.error(errMsg);
33530                     return;
33531                   } else {
33532                     throw new Error(errMsg);
33533                   }
33534                 }
33535               }
33536             }
33537             return out;
33538           }
33539         };
33540
33541         const {
33542           merge: merge$3,
33543           checkSanitizeDeprecation: checkSanitizeDeprecation$1,
33544           escape: escape$4
33545         } = helpers;
33546         const {
33547           getDefaults,
33548           changeDefaults,
33549           defaults: defaults$5
33550         } = defaults;
33551
33552         /**
33553          * Marked
33554          */
33555         function marked(src, opt, callback) {
33556           // throw error in case of non string input
33557           if (typeof src === 'undefined' || src === null) {
33558             throw new Error('marked(): input parameter is undefined or null');
33559           }
33560           if (typeof src !== 'string') {
33561             throw new Error('marked(): input parameter is of type '
33562               + Object.prototype.toString.call(src) + ', string expected');
33563           }
33564
33565           if (callback || typeof opt === 'function') {
33566             if (!callback) {
33567               callback = opt;
33568               opt = null;
33569             }
33570
33571             opt = merge$3({}, marked.defaults, opt || {});
33572             checkSanitizeDeprecation$1(opt);
33573             const highlight = opt.highlight;
33574             let tokens,
33575               pending,
33576               i = 0;
33577
33578             try {
33579               tokens = Lexer_1.lex(src, opt);
33580             } catch (e) {
33581               return callback(e);
33582             }
33583
33584             pending = tokens.length;
33585
33586             const done = function(err) {
33587               if (err) {
33588                 opt.highlight = highlight;
33589                 return callback(err);
33590               }
33591
33592               let out;
33593
33594               try {
33595                 out = Parser_1.parse(tokens, opt);
33596               } catch (e) {
33597                 err = e;
33598               }
33599
33600               opt.highlight = highlight;
33601
33602               return err
33603                 ? callback(err)
33604                 : callback(null, out);
33605             };
33606
33607             if (!highlight || highlight.length < 3) {
33608               return done();
33609             }
33610
33611             delete opt.highlight;
33612
33613             if (!pending) return done();
33614
33615             for (; i < tokens.length; i++) {
33616               (function(token) {
33617                 if (token.type !== 'code') {
33618                   return --pending || done();
33619                 }
33620                 return highlight(token.text, token.lang, function(err, code) {
33621                   if (err) return done(err);
33622                   if (code == null || code === token.text) {
33623                     return --pending || done();
33624                   }
33625                   token.text = code;
33626                   token.escaped = true;
33627                   --pending || done();
33628                 });
33629               })(tokens[i]);
33630             }
33631
33632             return;
33633           }
33634           try {
33635             opt = merge$3({}, marked.defaults, opt || {});
33636             checkSanitizeDeprecation$1(opt);
33637             return Parser_1.parse(Lexer_1.lex(src, opt), opt);
33638           } catch (e) {
33639             e.message += '\nPlease report this to https://github.com/markedjs/marked.';
33640             if ((opt || marked.defaults).silent) {
33641               return '<p>An error occurred:</p><pre>'
33642                 + escape$4(e.message + '', true)
33643                 + '</pre>';
33644             }
33645             throw e;
33646           }
33647         }
33648
33649         /**
33650          * Options
33651          */
33652
33653         marked.options =
33654         marked.setOptions = function(opt) {
33655           merge$3(marked.defaults, opt);
33656           changeDefaults(marked.defaults);
33657           return marked;
33658         };
33659
33660         marked.getDefaults = getDefaults;
33661
33662         marked.defaults = defaults$5;
33663
33664         /**
33665          * Use Extension
33666          */
33667
33668         marked.use = function(extension) {
33669           const opts = merge$3({}, extension);
33670           if (extension.renderer) {
33671             const renderer = marked.defaults.renderer || new Renderer_1();
33672             for (const prop in extension.renderer) {
33673               const prevRenderer = renderer[prop];
33674               renderer[prop] = (...args) => {
33675                 let ret = extension.renderer[prop].apply(renderer, args);
33676                 if (ret === false) {
33677                   ret = prevRenderer.apply(renderer, args);
33678                 }
33679                 return ret;
33680               };
33681             }
33682             opts.renderer = renderer;
33683           }
33684           if (extension.tokenizer) {
33685             const tokenizer = marked.defaults.tokenizer || new Tokenizer_1();
33686             for (const prop in extension.tokenizer) {
33687               const prevTokenizer = tokenizer[prop];
33688               tokenizer[prop] = (...args) => {
33689                 let ret = extension.tokenizer[prop].apply(tokenizer, args);
33690                 if (ret === false) {
33691                   ret = prevTokenizer.apply(tokenizer, args);
33692                 }
33693                 return ret;
33694               };
33695             }
33696             opts.tokenizer = tokenizer;
33697           }
33698           marked.setOptions(opts);
33699         };
33700
33701         /**
33702          * Expose
33703          */
33704
33705         marked.Parser = Parser_1;
33706         marked.parser = Parser_1.parse;
33707
33708         marked.Renderer = Renderer_1;
33709         marked.TextRenderer = TextRenderer_1;
33710
33711         marked.Lexer = Lexer_1;
33712         marked.lexer = Lexer_1.lex;
33713
33714         marked.Tokenizer = Tokenizer_1;
33715
33716         marked.Slugger = Slugger_1;
33717
33718         marked.parse = marked;
33719
33720         var marked_1 = marked;
33721
33722         const tiler$2 = utilTiler();
33723         const dispatch$3 = dispatch('loaded');
33724         const _tileZoom$2 = 14;
33725         const _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/api/0.3';
33726         let _osmoseData = { icons: {}, items: [] };
33727
33728         // This gets reassigned if reset
33729         let _cache$2;
33730
33731         function abortRequest$2(controller) {
33732           if (controller) {
33733             controller.abort();
33734           }
33735         }
33736
33737         function abortUnwantedRequests$2(cache, tiles) {
33738           Object.keys(cache.inflightTile).forEach(k => {
33739             let wanted = tiles.find(tile => k === tile.id);
33740             if (!wanted) {
33741               abortRequest$2(cache.inflightTile[k]);
33742               delete cache.inflightTile[k];
33743             }
33744           });
33745         }
33746
33747         function encodeIssueRtree$2(d) {
33748           return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
33749         }
33750
33751         // Replace or remove QAItem from rtree
33752         function updateRtree$2(item, replace) {
33753           _cache$2.rtree.remove(item, (a, b) => a.data.id === b.data.id);
33754
33755           if (replace) {
33756             _cache$2.rtree.insert(item);
33757           }
33758         }
33759
33760         // Issues shouldn't obscure eachother
33761         function preventCoincident$1(loc) {
33762           let coincident = false;
33763           do {
33764             // first time, move marker up. after that, move marker right.
33765             let delta = coincident ? [0.00001, 0] : [0, 0.00001];
33766             loc = geoVecAdd(loc, delta);
33767             let bbox = geoExtent(loc).bbox();
33768             coincident = _cache$2.rtree.search(bbox).length;
33769           } while (coincident);
33770
33771           return loc;
33772         }
33773
33774         var serviceOsmose = {
33775           title: 'osmose',
33776
33777           init() {
33778             _mainFileFetcher.get('qa_data')
33779               .then(d => {
33780                 _osmoseData = d.osmose;
33781                 _osmoseData.items = Object.keys(d.osmose.icons)
33782                   .map(s => s.split('-')[0])
33783                   .reduce((unique, item) => unique.indexOf(item) !== -1 ? unique : [...unique, item], []);
33784               });
33785
33786             if (!_cache$2) {
33787               this.reset();
33788             }
33789
33790             this.event = utilRebind(this, dispatch$3, 'on');
33791           },
33792
33793           reset() {
33794             let _strings = {};
33795             let _colors = {};
33796             if (_cache$2) {
33797               Object.values(_cache$2.inflightTile).forEach(abortRequest$2);
33798               // Strings and colors are static and should not be re-populated
33799               _strings = _cache$2.strings;
33800               _colors = _cache$2.colors;
33801             }
33802             _cache$2 = {
33803               data: {},
33804               loadedTile: {},
33805               inflightTile: {},
33806               inflightPost: {},
33807               closed: {},
33808               rtree: new RBush(),
33809               strings: _strings,
33810               colors: _colors
33811             };
33812           },
33813
33814           loadIssues(projection) {
33815             let params = {
33816               // Tiles return a maximum # of issues
33817               // So we want to filter our request for only types iD supports
33818               item: _osmoseData.items
33819             };
33820
33821             // determine the needed tiles to cover the view
33822             let tiles = tiler$2
33823               .zoomExtent([_tileZoom$2, _tileZoom$2])
33824               .getTiles(projection);
33825
33826             // abort inflight requests that are no longer needed
33827             abortUnwantedRequests$2(_cache$2, tiles);
33828
33829             // issue new requests..
33830             tiles.forEach(tile => {
33831               if (_cache$2.loadedTile[tile.id] || _cache$2.inflightTile[tile.id]) return;
33832
33833               let [ x, y, z ] = tile.xyz;
33834               let url = `${_osmoseUrlRoot}/issues/${z}/${x}/${y}.json?` + utilQsString(params);
33835
33836               let controller = new AbortController();
33837               _cache$2.inflightTile[tile.id] = controller;
33838
33839               d3_json(url, { signal: controller.signal })
33840                 .then(data => {
33841                   delete _cache$2.inflightTile[tile.id];
33842                   _cache$2.loadedTile[tile.id] = true;
33843
33844                   if (data.features) {
33845                     data.features.forEach(issue => {
33846                       const { item, class: cl, uuid: id } = issue.properties;
33847                       /* Osmose issues are uniquely identified by a unique
33848                         `item` and `class` combination (both integer values) */
33849                       const itemType = `${item}-${cl}`;
33850
33851                       // Filter out unsupported issue types (some are too specific or advanced)
33852                       if (itemType in _osmoseData.icons) {
33853                         let loc = issue.geometry.coordinates; // lon, lat
33854                         loc = preventCoincident$1(loc);
33855
33856                         let d = new QAItem(loc, this, itemType, id, { item });
33857
33858                         // Setting elems here prevents UI detail requests
33859                         if (item === 8300 || item === 8360) {
33860                           d.elems = [];
33861                         }
33862
33863                         _cache$2.data[d.id] = d;
33864                         _cache$2.rtree.insert(encodeIssueRtree$2(d));
33865                       }
33866                     });
33867                   }
33868
33869                   dispatch$3.call('loaded');
33870                 })
33871                 .catch(() => {
33872                   delete _cache$2.inflightTile[tile.id];
33873                   _cache$2.loadedTile[tile.id] = true;
33874                 });
33875             });
33876           },
33877
33878           loadIssueDetail(issue) {
33879             // Issue details only need to be fetched once
33880             if (issue.elems !== undefined) {
33881               return Promise.resolve(issue);
33882             }
33883
33884             const url = `${_osmoseUrlRoot}/issue/${issue.id}?langs=${_mainLocalizer.localeCode()}`;
33885             const cacheDetails = data => {
33886               // Associated elements used for highlighting
33887               // Assign directly for immediate use in the callback
33888               issue.elems = data.elems.map(e => e.type.substring(0,1) + e.id);
33889
33890               // Some issues have instance specific detail in a subtitle
33891               issue.detail = data.subtitle ? marked_1(data.subtitle.auto) : '';
33892
33893               this.replaceItem(issue);
33894             };
33895
33896             return d3_json(url).then(cacheDetails).then(() => issue);
33897           },
33898
33899           loadStrings(locale=_mainLocalizer.localeCode()) {
33900             const items = Object.keys(_osmoseData.icons);
33901
33902             if (
33903               locale in _cache$2.strings
33904               && Object.keys(_cache$2.strings[locale]).length === items.length
33905             ) {
33906                 return Promise.resolve(_cache$2.strings[locale]);
33907             }
33908
33909             // May be partially populated already if some requests were successful
33910             if (!(locale in _cache$2.strings)) {
33911               _cache$2.strings[locale] = {};
33912             }
33913
33914             // Only need to cache strings for supported issue types
33915             // Using multiple individual item + class requests to reduce fetched data size
33916             const allRequests = items.map(itemType => {
33917               // No need to request data we already have
33918               if (itemType in _cache$2.strings[locale]) return;
33919
33920               const cacheData = data => {
33921                 // Bunch of nested single value arrays of objects
33922                 const [ cat = {items:[]} ] = data.categories;
33923                 const [ item = {class:[]} ] = cat.items;
33924                 const [ cl = null ] = item.class;
33925
33926                 // If null default value is reached, data wasn't as expected (or was empty)
33927                 if (!cl) {
33928                   /* eslint-disable no-console */
33929                   console.log(`Osmose strings request (${itemType}) had unexpected data`);
33930                   /* eslint-enable no-console */
33931                   return;
33932                 }
33933
33934                 // Cache served item colors to automatically style issue markers later
33935                 const { item: itemInt, color } = item;
33936                 if (/^#[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/.test(color)) {
33937                   _cache$2.colors[itemInt] = color;
33938                 }
33939
33940                 // Value of root key will be null if no string exists
33941                 // If string exists, value is an object with key 'auto' for string
33942                 const { title, detail, fix, trap } = cl;
33943
33944                 // Osmose titles shouldn't contain markdown
33945                 let issueStrings = {};
33946                 if (title) issueStrings.title = title.auto;
33947                 if (detail) issueStrings.detail = marked_1(detail.auto);
33948                 if (trap) issueStrings.trap = marked_1(trap.auto);
33949                 if (fix) issueStrings.fix = marked_1(fix.auto);
33950
33951                 _cache$2.strings[locale][itemType] = issueStrings;
33952               };
33953
33954               const [ item, cl ] = itemType.split('-');
33955
33956               // Osmose API falls back to English strings where untranslated or if locale doesn't exist
33957               const url = `${_osmoseUrlRoot}/items/${item}/class/${cl}?langs=${locale}`;
33958
33959               return d3_json(url).then(cacheData);
33960             });
33961
33962             return Promise.all(allRequests).then(() => _cache$2.strings[locale]);
33963           },
33964
33965           getStrings(itemType, locale=_mainLocalizer.localeCode()) {
33966             // No need to fallback to English, Osmose API handles this for us
33967             return (locale in _cache$2.strings) ? _cache$2.strings[locale][itemType] : {};
33968           },
33969
33970           getColor(itemType) {
33971             return (itemType in _cache$2.colors) ? _cache$2.colors[itemType] : '#FFFFFF';
33972           },
33973
33974           postUpdate(issue, callback) {
33975             if (_cache$2.inflightPost[issue.id]) {
33976               return callback({ message: 'Issue update already inflight', status: -2 }, issue);
33977             }
33978
33979             // UI sets the status to either 'done' or 'false'
33980             const url = `${_osmoseUrlRoot}/issue/${issue.id}/${issue.newStatus}`;
33981             const controller = new AbortController();
33982             const after = () => {
33983               delete _cache$2.inflightPost[issue.id];
33984
33985               this.removeItem(issue);
33986               if (issue.newStatus === 'done') {
33987                 // Keep track of the number of issues closed per `item` to tag the changeset
33988                 if (!(issue.item in _cache$2.closed)) {
33989                   _cache$2.closed[issue.item] = 0;
33990                 }
33991                 _cache$2.closed[issue.item] += 1;
33992               }
33993               if (callback) callback(null, issue);
33994             };
33995
33996             _cache$2.inflightPost[issue.id] = controller;
33997
33998             fetch(url, { signal: controller.signal })
33999               .then(after)
34000               .catch(err => {
34001                 delete _cache$2.inflightPost[issue.id];
34002                 if (callback) callback(err.message);
34003               });
34004           },
34005
34006           // Get all cached QAItems covering the viewport
34007           getItems(projection) {
34008             const viewport = projection.clipExtent();
34009             const min = [viewport[0][0], viewport[1][1]];
34010             const max = [viewport[1][0], viewport[0][1]];
34011             const bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
34012
34013             return _cache$2.rtree.search(bbox).map(d => d.data);
34014           },
34015
34016           // Get a QAItem from cache
34017           // NOTE: Don't change method name until UI v3 is merged
34018           getError(id) {
34019             return _cache$2.data[id];
34020           },
34021
34022           // get the name of the icon to display for this item
34023           getIcon(itemType) {
34024             return _osmoseData.icons[itemType];
34025           },
34026
34027           // Replace a single QAItem in the cache
34028           replaceItem(item) {
34029             if (!(item instanceof QAItem) || !item.id) return;
34030
34031             _cache$2.data[item.id] = item;
34032             updateRtree$2(encodeIssueRtree$2(item), true); // true = replace
34033             return item;
34034           },
34035
34036           // Remove a single QAItem from the cache
34037           removeItem(item) {
34038             if (!(item instanceof QAItem) || !item.id) return;
34039
34040             delete _cache$2.data[item.id];
34041             updateRtree$2(encodeIssueRtree$2(item), false); // false = remove
34042           },
34043
34044           // Used to populate `closed:osmose:*` changeset tags
34045           getClosedCounts() {
34046             return _cache$2.closed;
34047           },
34048
34049           itemURL(item) {
34050             return `https://osmose.openstreetmap.fr/en/error/${item.id}`;
34051           }
34052         };
34053
34054         /*
34055             A standalone SVG element that contains only a `defs` sub-element. To be
34056             used once globally, since defs IDs must be unique within a document.
34057         */
34058         function svgDefs(context) {
34059
34060             function drawDefs(selection) {
34061                 var defs = selection.append('defs');
34062
34063                 // add markers
34064                 defs
34065                     .append('marker')
34066                     .attr('id', 'ideditor-oneway-marker')
34067                     .attr('viewBox', '0 0 10 5')
34068                     .attr('refX', 2.5)
34069                     .attr('refY', 2.5)
34070                     .attr('markerWidth', 2)
34071                     .attr('markerHeight', 2)
34072                     .attr('markerUnits', 'strokeWidth')
34073                     .attr('orient', 'auto')
34074                     .append('path')
34075                     .attr('class', 'oneway-marker-path')
34076                     .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')
34077                     .attr('stroke', 'none')
34078                     .attr('fill', '#000')
34079                     .attr('opacity', '0.75');
34080
34081                 // SVG markers have to be given a colour where they're defined
34082                 // (they can't inherit it from the line they're attached to),
34083                 // so we need to manually define markers for each color of tag
34084                 // (also, it's slightly nicer if we can control the
34085                 // positioning for different tags)
34086                 function addSidedMarker(name, color, offset) {
34087                     defs
34088                         .append('marker')
34089                         .attr('id', 'ideditor-sided-marker-' + name)
34090                         .attr('viewBox', '0 0 2 2')
34091                         .attr('refX', 1)
34092                         .attr('refY', -offset)
34093                         .attr('markerWidth', 1.5)
34094                         .attr('markerHeight', 1.5)
34095                         .attr('markerUnits', 'strokeWidth')
34096                         .attr('orient', 'auto')
34097                         .append('path')
34098                         .attr('class', 'sided-marker-path sided-marker-' + name + '-path')
34099                         .attr('d', 'M 0,0 L 1,1 L 2,0 z')
34100                         .attr('stroke', 'none')
34101                         .attr('fill', color);
34102                 }
34103                 addSidedMarker('natural', 'rgb(170, 170, 170)', 0);
34104                 // for a coastline, the arrows are (somewhat unintuitively) on
34105                 // the water side, so let's color them blue (with a gap) to
34106                 // give a stronger indication
34107                 addSidedMarker('coastline', '#77dede', 1);
34108                 addSidedMarker('waterway', '#77dede', 1);
34109                 // barriers have a dashed line, and separating the triangle
34110                 // from the line visually suits that
34111                 addSidedMarker('barrier', '#ddd', 1);
34112                 addSidedMarker('man_made', '#fff', 0);
34113
34114                 defs
34115                     .append('marker')
34116                     .attr('id', 'ideditor-viewfield-marker')
34117                     .attr('viewBox', '0 0 16 16')
34118                     .attr('refX', 8)
34119                     .attr('refY', 16)
34120                     .attr('markerWidth', 4)
34121                     .attr('markerHeight', 4)
34122                     .attr('markerUnits', 'strokeWidth')
34123                     .attr('orient', 'auto')
34124                     .append('path')
34125                     .attr('class', 'viewfield-marker-path')
34126                     .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')
34127                     .attr('fill', '#333')
34128                     .attr('fill-opacity', '0.75')
34129                     .attr('stroke', '#fff')
34130                     .attr('stroke-width', '0.5px')
34131                     .attr('stroke-opacity', '0.75');
34132
34133                 defs
34134                     .append('marker')
34135                     .attr('id', 'ideditor-viewfield-marker-wireframe')
34136                     .attr('viewBox', '0 0 16 16')
34137                     .attr('refX', 8)
34138                     .attr('refY', 16)
34139                     .attr('markerWidth', 4)
34140                     .attr('markerHeight', 4)
34141                     .attr('markerUnits', 'strokeWidth')
34142                     .attr('orient', 'auto')
34143                     .append('path')
34144                     .attr('class', 'viewfield-marker-path')
34145                     .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')
34146                     .attr('fill', 'none')
34147                     .attr('stroke', '#fff')
34148                     .attr('stroke-width', '0.5px')
34149                     .attr('stroke-opacity', '0.75');
34150
34151                 // add patterns
34152                 var patterns = defs.selectAll('pattern')
34153                     .data([
34154                         // pattern name, pattern image name
34155                         ['beach', 'dots'],
34156                         ['construction', 'construction'],
34157                         ['cemetery', 'cemetery'],
34158                         ['cemetery_christian', 'cemetery_christian'],
34159                         ['cemetery_buddhist', 'cemetery_buddhist'],
34160                         ['cemetery_muslim', 'cemetery_muslim'],
34161                         ['cemetery_jewish', 'cemetery_jewish'],
34162                         ['farmland', 'farmland'],
34163                         ['farmyard', 'farmyard'],
34164                         ['forest', 'forest'],
34165                         ['forest_broadleaved', 'forest_broadleaved'],
34166                         ['forest_needleleaved', 'forest_needleleaved'],
34167                         ['forest_leafless', 'forest_leafless'],
34168                         ['golf_green', 'grass'],
34169                         ['grass', 'grass'],
34170                         ['landfill', 'landfill'],
34171                         ['meadow', 'grass'],
34172                         ['orchard', 'orchard'],
34173                         ['pond', 'pond'],
34174                         ['quarry', 'quarry'],
34175                         ['scrub', 'bushes'],
34176                         ['vineyard', 'vineyard'],
34177                         ['water_standing', 'lines'],
34178                         ['waves', 'waves'],
34179                         ['wetland', 'wetland'],
34180                         ['wetland_marsh', 'wetland_marsh'],
34181                         ['wetland_swamp', 'wetland_swamp'],
34182                         ['wetland_bog', 'wetland_bog'],
34183                         ['wetland_reedbed', 'wetland_reedbed']
34184                     ])
34185                     .enter()
34186                     .append('pattern')
34187                     .attr('id', function (d) { return 'ideditor-pattern-' + d[0]; })
34188                     .attr('width', 32)
34189                     .attr('height', 32)
34190                     .attr('patternUnits', 'userSpaceOnUse');
34191
34192                 patterns
34193                     .append('rect')
34194                     .attr('x', 0)
34195                     .attr('y', 0)
34196                     .attr('width', 32)
34197                     .attr('height', 32)
34198                     .attr('class', function (d) { return 'pattern-color-' + d[0]; });
34199
34200                 patterns
34201                     .append('image')
34202                     .attr('x', 0)
34203                     .attr('y', 0)
34204                     .attr('width', 32)
34205                     .attr('height', 32)
34206                     .attr('xlink:href', function (d) {
34207                         return context.imagePath('pattern/' + d[1] + '.png');
34208                     });
34209
34210                 // add clip paths
34211                 defs.selectAll('clipPath')
34212                     .data([12, 18, 20, 32, 45])
34213                     .enter()
34214                     .append('clipPath')
34215                     .attr('id', function (d) { return 'ideditor-clip-square-' + d; })
34216                     .append('rect')
34217                     .attr('x', 0)
34218                     .attr('y', 0)
34219                     .attr('width', function (d) { return d; })
34220                     .attr('height', function (d) { return d; });
34221
34222                 // add symbol spritesheets
34223                 defs
34224                     .call(drawDefs.addSprites, [
34225                         'iD-sprite', 'maki-sprite', 'temaki-sprite', 'fa-sprite', 'tnp-sprite', 'community-sprite'
34226                     ], true);
34227             }
34228
34229
34230             drawDefs.addSprites = function(selection, ids, overrideColors) {
34231                 var spritesheets = selection.selectAll('.spritesheet');
34232                 var currData = spritesheets.data();
34233                 var data = utilArrayUniq(currData.concat(ids));
34234
34235                 spritesheets
34236                     .data(data)
34237                     .enter()
34238                     .append('g')
34239                     .attr('class', function(d) { return 'spritesheet spritesheet-' + d; })
34240                     .each(function(d) {
34241                         var url = context.imagePath(d + '.svg');
34242                         var node = select(this).node();
34243
34244                         svg(url)
34245                             .then(function(svg) {
34246                                 node.appendChild(
34247                                     select(svg.documentElement).attr('id', 'ideditor-' + d).node()
34248                                 );
34249                                 if (overrideColors && d !== 'iD-sprite') {   // allow icon colors to be overridden..
34250                                     select(node).selectAll('path')
34251                                         .attr('fill', 'currentColor');
34252                                 }
34253                             })
34254                             .catch(function() {
34255                                 /* ignore */
34256                             });
34257                     });
34258             };
34259
34260
34261             return drawDefs;
34262         }
34263
34264         /* global Mapillary:false */
34265
34266
34267         var apibase = 'https://a.mapillary.com/v3/';
34268         var viewercss = 'mapillary-js/mapillary.min.css';
34269         var viewerjs = 'mapillary-js/mapillary.min.js';
34270         var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi';
34271         var mapFeatureConfig = {
34272             values: [
34273                 'construction--flat--crosswalk-plain',
34274                 'marking--discrete--crosswalk-zebra',
34275                 'object--banner',
34276                 'object--bench',
34277                 'object--bike-rack',
34278                 'object--billboard',
34279                 'object--catch-basin',
34280                 'object--cctv-camera',
34281                 'object--fire-hydrant',
34282                 'object--mailbox',
34283                 'object--manhole',
34284                 'object--phone-booth',
34285                 'object--sign--advertisement',
34286                 'object--sign--information',
34287                 'object--sign--store',
34288                 'object--street-light',
34289                 'object--support--utility-pole',
34290                 'object--traffic-light--*',
34291                 'object--traffic-light--pedestrians',
34292                 'object--trash-can'
34293             ].join(',')
34294         };
34295         var maxResults = 1000;
34296         var tileZoom = 14;
34297         var tiler$3 = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true);
34298         var dispatch$4 = dispatch('loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged');
34299         var _mlyFallback = false;
34300         var _mlyCache;
34301         var _mlyClicks;
34302         var _mlySelectedImageKey;
34303         var _mlyViewer;
34304
34305
34306         function abortRequest$3(controller) {
34307             controller.abort();
34308         }
34309
34310
34311         function maxPageAtZoom(z) {
34312             if (z < 15)   return 2;
34313             if (z === 15) return 5;
34314             if (z === 16) return 10;
34315             if (z === 17) return 20;
34316             if (z === 18) return 40;
34317             if (z > 18)   return 80;
34318         }
34319
34320
34321         function loadTiles(which, url, projection) {
34322             var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
34323             var tiles = tiler$3.getTiles(projection);
34324
34325             // abort inflight requests that are no longer needed
34326             var cache = _mlyCache[which];
34327             Object.keys(cache.inflight).forEach(function(k) {
34328                 var wanted = tiles.find(function(tile) { return k.indexOf(tile.id + ',') === 0; });
34329                 if (!wanted) {
34330                     abortRequest$3(cache.inflight[k]);
34331                     delete cache.inflight[k];
34332                 }
34333             });
34334
34335             tiles.forEach(function(tile) {
34336                 loadNextTilePage(which, currZoom, url, tile);
34337             });
34338         }
34339
34340
34341         function loadNextTilePage(which, currZoom, url, tile) {
34342             var cache = _mlyCache[which];
34343             var rect = tile.extent.rectangle();
34344             var maxPages = maxPageAtZoom(currZoom);
34345             var nextPage = cache.nextPage[tile.id] || 0;
34346             var nextURL = cache.nextURL[tile.id] || url +
34347                 utilQsString({
34348                     per_page: maxResults,
34349                     page: nextPage,
34350                     client_id: clientId,
34351                     bbox: [rect[0], rect[1], rect[2], rect[3]].join(','),
34352                 });
34353
34354             if (nextPage > maxPages) return;
34355
34356             var id = tile.id + ',' + String(nextPage);
34357             if (cache.loaded[id] || cache.inflight[id]) return;
34358
34359             var controller = new AbortController();
34360             cache.inflight[id] = controller;
34361
34362             var options = {
34363                 method: 'GET',
34364                 signal: controller.signal,
34365                 headers: { 'Content-Type': 'application/json' }
34366             };
34367
34368             fetch(nextURL, options)
34369                 .then(function(response) {
34370                     if (!response.ok) {
34371                         throw new Error(response.status + ' ' + response.statusText);
34372                     }
34373                     var linkHeader = response.headers.get('Link');
34374                     if (linkHeader) {
34375                         var pagination = parsePagination(linkHeader);
34376                         if (pagination.next) {
34377                             cache.nextURL[tile.id] = pagination.next;
34378                         }
34379                     }
34380                     return response.json();
34381                 })
34382                 .then(function(data) {
34383                     cache.loaded[id] = true;
34384                     delete cache.inflight[id];
34385                     if (!data || !data.features || !data.features.length) {
34386                         throw new Error('No Data');
34387                     }
34388
34389                     var features = data.features.map(function(feature) {
34390                         var loc = feature.geometry.coordinates;
34391                         var d;
34392
34393                         // An image (shown as a green dot on the map) is a single street photo with extra
34394                         // information such as location, camera angle (CA), camera model, and so on.
34395                         // Each image feature is a GeoJSON Point
34396                         if (which === 'images') {
34397                             d = {
34398                                 loc: loc,
34399                                 key: feature.properties.key,
34400                                 ca: feature.properties.ca,
34401                                 captured_at: feature.properties.captured_at,
34402                                 captured_by: feature.properties.username,
34403                                 pano: feature.properties.pano
34404                             };
34405
34406                             cache.forImageKey[d.key] = d;     // cache imageKey -> image
34407
34408                         // Mapillary organizes images as sequences. A sequence of images are continuously captured
34409                         // by a user at a give time. Sequences are shown on the map as green lines.
34410                         // Each sequence feature is a GeoJSON LineString
34411                         } else if (which === 'sequences') {
34412                             var sequenceKey = feature.properties.key;
34413                             cache.lineString[sequenceKey] = feature;           // cache sequenceKey -> lineString
34414                             feature.properties.coordinateProperties.image_keys.forEach(function(imageKey) {
34415                                 cache.forImageKey[imageKey] = sequenceKey;     // cache imageKey -> sequenceKey
34416                             });
34417                             return false;    // because no `d` data worth loading into an rbush
34418
34419                         // An image detection is a semantic pixel area on an image. The area could indicate
34420                         // sky, trees, sidewalk in the image. A detection can be a polygon, a bounding box, or a point.
34421                         // Each image_detection feature is a GeoJSON Point (located where the image was taken)
34422                         } else if (which === 'image_detections') {
34423                             d = {
34424                                 key: feature.properties.key,
34425                                 image_key: feature.properties.image_key,
34426                                 value: feature.properties.value,
34427                                 package: feature.properties.package,
34428                                 shape: feature.properties.shape
34429                             };
34430
34431                             // cache imageKey -> image_detections
34432                             if (!cache.forImageKey[d.image_key]) {
34433                                 cache.forImageKey[d.image_key] = [];
34434                             }
34435                             cache.forImageKey[d.image_key].push(d);
34436                             return false;    // because no `d` data worth loading into an rbush
34437
34438
34439                         // A map feature is a real world object that can be shown on a map. It could be any object
34440                         // recognized from images, manually added in images, or added on the map.
34441                         // Each map feature is a GeoJSON Point (located where the feature is)
34442                         } else if (which === 'map_features' || which === 'points') {
34443                             d = {
34444                                 loc: loc,
34445                                 key: feature.properties.key,
34446                                 value: feature.properties.value,
34447                                 package: feature.properties.package,
34448                                 detections: feature.properties.detections
34449                             };
34450                         }
34451
34452                         return {
34453                             minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
34454                         };
34455
34456                     }).filter(Boolean);
34457
34458                     if (cache.rtree && features) {
34459                         cache.rtree.load(features);
34460                     }
34461
34462                     if (data.features.length === maxResults) {  // more pages to load
34463                         cache.nextPage[tile.id] = nextPage + 1;
34464                         loadNextTilePage(which, currZoom, url, tile);
34465                     } else {
34466                         cache.nextPage[tile.id] = Infinity;     // no more pages to load
34467                     }
34468
34469                     if (which === 'images' || which === 'sequences') {
34470                         dispatch$4.call('loadedImages');
34471                     } else if (which === 'map_features') {
34472                         dispatch$4.call('loadedSigns');
34473                     } else if (which === 'points') {
34474                         dispatch$4.call('loadedMapFeatures');
34475                     }
34476                 })
34477                 .catch(function() {
34478                     cache.loaded[id] = true;
34479                     delete cache.inflight[id];
34480                 });
34481         }
34482
34483         // extract links to pages of API results
34484         function parsePagination(links) {
34485             return links.split(',').map(function(rel) {
34486                 var elements = rel.split(';');
34487                 if (elements.length === 2) {
34488                     return [
34489                         /<(.+)>/.exec(elements[0])[1],
34490                         /rel="(.+)"/.exec(elements[1])[1]
34491                     ];
34492                 } else {
34493                     return ['',''];
34494                 }
34495             }).reduce(function(pagination, val) {
34496                 pagination[val[1]] = val[0];
34497                 return pagination;
34498             }, {});
34499         }
34500
34501
34502         // partition viewport into higher zoom tiles
34503         function partitionViewport(projection) {
34504             var z = geoScaleToZoom(projection.scale());
34505             var z2 = (Math.ceil(z * 2) / 2) + 2.5;   // round to next 0.5 and add 2.5
34506             var tiler = utilTiler().zoomExtent([z2, z2]);
34507
34508             return tiler.getTiles(projection)
34509                 .map(function(tile) { return tile.extent; });
34510         }
34511
34512
34513         // no more than `limit` results per partition.
34514         function searchLimited(limit, projection, rtree) {
34515             limit = limit || 5;
34516
34517             return partitionViewport(projection)
34518                 .reduce(function(result, extent) {
34519                     var found = rtree.search(extent.bbox())
34520                         .slice(0, limit)
34521                         .map(function(d) { return d.data; });
34522
34523                     return (found.length ? result.concat(found) : result);
34524                 }, []);
34525         }
34526
34527
34528
34529         var serviceMapillary = {
34530
34531             init: function() {
34532                 if (!_mlyCache) {
34533                     this.reset();
34534                 }
34535
34536                 this.event = utilRebind(this, dispatch$4, 'on');
34537             },
34538
34539             reset: function() {
34540                 if (_mlyCache) {
34541                     Object.values(_mlyCache.images.inflight).forEach(abortRequest$3);
34542                     Object.values(_mlyCache.image_detections.inflight).forEach(abortRequest$3);
34543                     Object.values(_mlyCache.map_features.inflight).forEach(abortRequest$3);
34544                     Object.values(_mlyCache.points.inflight).forEach(abortRequest$3);
34545                     Object.values(_mlyCache.sequences.inflight).forEach(abortRequest$3);
34546                 }
34547
34548                 _mlyCache = {
34549                     images: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush(), forImageKey: {} },
34550                     image_detections: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, forImageKey: {} },
34551                     map_features: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush() },
34552                     points: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush() },
34553                     sequences: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush(), forImageKey: {}, lineString: {} }
34554                 };
34555
34556                 _mlySelectedImageKey = null;
34557                 _mlyClicks = [];
34558             },
34559
34560
34561             images: function(projection) {
34562                 var limit = 5;
34563                 return searchLimited(limit, projection, _mlyCache.images.rtree);
34564             },
34565
34566
34567             signs: function(projection) {
34568                 var limit = 5;
34569                 return searchLimited(limit, projection, _mlyCache.map_features.rtree);
34570             },
34571
34572
34573             mapFeatures: function(projection) {
34574                 var limit = 5;
34575                 return searchLimited(limit, projection, _mlyCache.points.rtree);
34576             },
34577
34578
34579             cachedImage: function(imageKey) {
34580                 return _mlyCache.images.forImageKey[imageKey];
34581             },
34582
34583
34584             sequences: function(projection) {
34585                 var viewport = projection.clipExtent();
34586                 var min = [viewport[0][0], viewport[1][1]];
34587                 var max = [viewport[1][0], viewport[0][1]];
34588                 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
34589                 var sequenceKeys = {};
34590
34591                 // all sequences for images in viewport
34592                 _mlyCache.images.rtree.search(bbox)
34593                     .forEach(function(d) {
34594                         var sequenceKey = _mlyCache.sequences.forImageKey[d.data.key];
34595                         if (sequenceKey) {
34596                             sequenceKeys[sequenceKey] = true;
34597                         }
34598                     });
34599
34600                 // Return lineStrings for the sequences
34601                 return Object.keys(sequenceKeys).map(function(sequenceKey) {
34602                     return _mlyCache.sequences.lineString[sequenceKey];
34603                 });
34604             },
34605
34606
34607             signsSupported: function() {
34608                 return true;
34609             },
34610
34611
34612             loadImages: function(projection) {
34613                 loadTiles('images', apibase + 'images?sort_by=key&', projection);
34614                 loadTiles('sequences', apibase + 'sequences?sort_by=key&', projection);
34615             },
34616
34617
34618             loadSigns: 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('map_features', apibase + 'map_features?layers=trafficsigns&min_nbr_image_detections=2&sort_by=key&', projection);
34622                 loadTiles('image_detections', apibase + 'image_detections?layers=trafficsigns&sort_by=key&', projection);
34623             },
34624
34625
34626             loadMapFeatures: function(projection) {
34627                 // if we are looking at signs, we'll actually need to fetch images too
34628                 loadTiles('images', apibase + 'images?sort_by=key', projection);
34629                 loadTiles('points', apibase + 'map_features?layers=points&min_nbr_image_detections=2&sort_by=key&values=' + mapFeatureConfig.values + '&', projection);
34630                 loadTiles('image_detections', apibase + 'image_detections?layers=points&sort_by=key&values=' + mapFeatureConfig.values + '&', projection);
34631             },
34632
34633
34634             loadViewer: function(context) {
34635                 // add mly-wrapper
34636                 var wrap = context.container().select('.photoviewer')
34637                     .selectAll('.mly-wrapper')
34638                     .data([0]);
34639
34640                 wrap.enter()
34641                     .append('div')
34642                     .attr('id', 'ideditor-mly')
34643                     .attr('class', 'photo-wrapper mly-wrapper')
34644                     .classed('hide', true);
34645
34646                 // load mapillary-viewercss
34647                 select('head').selectAll('#ideditor-mapillary-viewercss')
34648                     .data([0])
34649                     .enter()
34650                     .append('link')
34651                     .attr('id', 'ideditor-mapillary-viewercss')
34652                     .attr('rel', 'stylesheet')
34653                     .attr('href', context.asset(viewercss));
34654
34655                 // load mapillary-viewerjs
34656                 select('head').selectAll('#ideditor-mapillary-viewerjs')
34657                     .data([0])
34658                     .enter()
34659                     .append('script')
34660                     .attr('id', 'ideditor-mapillary-viewerjs')
34661                     .attr('src', context.asset(viewerjs));
34662
34663                 // load mapillary signs sprite
34664                 var defs = context.container().select('defs');
34665                 defs.call(svgDefs(context).addSprites, ['mapillary-sprite', 'mapillary-object-sprite'], false /* don't override colors */ );
34666
34667                 // Register viewer resize handler
34668                 context.ui().photoviewer.on('resize.mapillary', function() {
34669                     if (_mlyViewer) {
34670                         _mlyViewer.resize();
34671                     }
34672                 });
34673             },
34674
34675
34676             showViewer: function(context) {
34677                 var wrap = context.container().select('.photoviewer')
34678                     .classed('hide', false);
34679
34680                 var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size();
34681
34682                 if (isHidden && _mlyViewer) {
34683                     wrap
34684                         .selectAll('.photo-wrapper:not(.mly-wrapper)')
34685                         .classed('hide', true);
34686
34687                     wrap
34688                         .selectAll('.photo-wrapper.mly-wrapper')
34689                         .classed('hide', false);
34690
34691                     _mlyViewer.resize();
34692                 }
34693
34694                 return this;
34695             },
34696
34697
34698             hideViewer: function(context) {
34699                 _mlySelectedImageKey = null;
34700
34701                 if (!_mlyFallback && _mlyViewer) {
34702                     _mlyViewer.getComponent('sequence').stop();
34703                 }
34704
34705                 var viewer = context.container().select('.photoviewer');
34706                 if (!viewer.empty()) viewer.datum(null);
34707
34708                 viewer
34709                     .classed('hide', true)
34710                     .selectAll('.photo-wrapper')
34711                     .classed('hide', true);
34712
34713                 context.container().selectAll('.viewfield-group, .sequence, .icon-detected')
34714                     .classed('currentView', false);
34715
34716                 return this.setStyles(context, null, true);
34717             },
34718
34719
34720             parsePagination: parsePagination,
34721
34722
34723             updateViewer: function(context, imageKey) {
34724                 if (!imageKey) return this;
34725
34726                 if (!_mlyViewer) {
34727                     this.initViewer(context, imageKey);
34728                 } else {
34729                     _mlyViewer.moveToKey(imageKey)
34730                         .catch(function(e) { console.error('mly3', e); });  // eslint-disable-line no-console
34731                 }
34732
34733                 return this;
34734             },
34735
34736
34737             initViewer: function(context, imageKey) {
34738                 var that = this;
34739                 if (window.Mapillary && imageKey) {
34740                     var opts = {
34741                         baseImageSize: 320,
34742                         component: {
34743                             cover: false,
34744                             keyboard: false,
34745                             tag: true
34746                         }
34747                     };
34748
34749                     // Disable components requiring WebGL support
34750                     if (!Mapillary.isSupported() && Mapillary.isFallbackSupported()) {
34751                         _mlyFallback = true;
34752                         opts.component = {
34753                             cover: false,
34754                             direction: false,
34755                             imagePlane: false,
34756                             keyboard: false,
34757                             mouse: false,
34758                             sequence: false,
34759                             tag: false,
34760                             image: true,        // fallback
34761                             navigation: true    // fallback
34762                         };
34763                     }
34764
34765                     _mlyViewer = new Mapillary.Viewer('ideditor-mly', clientId, null, opts);
34766                     _mlyViewer.on('nodechanged', nodeChanged);
34767                     _mlyViewer.on('bearingchanged', bearingChanged);
34768                     _mlyViewer.moveToKey(imageKey)
34769                         .catch(function(e) { console.error('mly3', e); });  // eslint-disable-line no-console
34770                 }
34771
34772                 // nodeChanged: called after the viewer has changed images and is ready.
34773                 //
34774                 // There is some logic here to batch up clicks into a _mlyClicks array
34775                 // because the user might click on a lot of markers quickly and nodechanged
34776                 // may be called out of order asychronously.
34777                 //
34778                 // Clicks are added to the array in `selectedImage` and removed here.
34779                 //
34780                 function nodeChanged(node) {
34781                     if (!_mlyFallback) {
34782                         _mlyViewer.getComponent('tag').removeAll();  // remove previous detections
34783                     }
34784
34785                     var clicks = _mlyClicks;
34786                     var index = clicks.indexOf(node.key);
34787                     var selectedKey = _mlySelectedImageKey;
34788
34789                     if (index > -1) {              // `nodechanged` initiated from clicking on a marker..
34790                         clicks.splice(index, 1);   // remove the click
34791                         // If `node.key` matches the current _mlySelectedImageKey, call `selectImage()`
34792                         // one more time to update the detections and attribution..
34793                         if (node.key === selectedKey) {
34794                             that.selectImage(context, _mlySelectedImageKey, true);
34795                         }
34796                     } else {             // `nodechanged` initiated from the Mapillary viewer controls..
34797                         var loc = node.computedLatLon ? [node.computedLatLon.lon, node.computedLatLon.lat] : [node.latLon.lon, node.latLon.lat];
34798                         context.map().centerEase(loc);
34799                         that.selectImage(context, node.key, true);
34800                     }
34801                 }
34802
34803                 function bearingChanged(e) {
34804                     dispatch$4.call('bearingChanged', undefined, e);
34805                 }
34806             },
34807
34808
34809             // Pass in the image key string as `imageKey`.
34810             // This allows images to be selected from places that dont have access
34811             // to the full image datum (like the street signs layer or the js viewer)
34812             selectImage: function(context, imageKey, fromViewer) {
34813
34814                 _mlySelectedImageKey = imageKey;
34815
34816                 // Note the datum could be missing, but we'll try to carry on anyway.
34817                 // There just might be a delay before user sees detections, captured_at, etc.
34818                 var d = _mlyCache.images.forImageKey[imageKey];
34819
34820                 var viewer = context.container().select('.photoviewer');
34821                 if (!viewer.empty()) viewer.datum(d);
34822
34823                 imageKey = (d && d.key) || imageKey;
34824                 if (!fromViewer && imageKey) {
34825                     _mlyClicks.push(imageKey);
34826                 }
34827
34828                 this.setStyles(context, null, true);
34829
34830                 // if signs signs are shown, highlight the ones that appear in this image
34831                 context.container().selectAll('.layer-mapillary-signs .icon-detected')
34832                     .classed('currentView', function(d) {
34833                         return d.detections.some(function(detection) {
34834                             return detection.image_key === imageKey;
34835                         });
34836                     });
34837
34838                 if (d) {
34839                     this.updateDetections(d);
34840                 }
34841
34842                 return this;
34843             },
34844
34845
34846             getSelectedImageKey: function() {
34847                 return _mlySelectedImageKey;
34848             },
34849
34850
34851             getSequenceKeyForImageKey: function(imageKey) {
34852                 return _mlyCache.sequences.forImageKey[imageKey];
34853             },
34854
34855
34856             // Updates the currently highlighted sequence and selected bubble.
34857             // Reset is only necessary when interacting with the viewport because
34858             // this implicitly changes the currently selected bubble/sequence
34859             setStyles: function(context, hovered, reset) {
34860                 if (reset) {  // reset all layers
34861                     context.container().selectAll('.viewfield-group')
34862                         .classed('highlighted', false)
34863                         .classed('hovered', false)
34864                         .classed('currentView', false);
34865
34866                     context.container().selectAll('.sequence')
34867                         .classed('highlighted', false)
34868                         .classed('currentView', false);
34869                 }
34870
34871                 var hoveredImageKey = hovered && hovered.key;
34872                 var hoveredSequenceKey = hoveredImageKey && this.getSequenceKeyForImageKey(hoveredImageKey);
34873                 var hoveredLineString = hoveredSequenceKey && _mlyCache.sequences.lineString[hoveredSequenceKey];
34874                 var hoveredImageKeys = (hoveredLineString && hoveredLineString.properties.coordinateProperties.image_keys) || [];
34875
34876                 var selectedImageKey = _mlySelectedImageKey;
34877                 var selectedSequenceKey = selectedImageKey && this.getSequenceKeyForImageKey(selectedImageKey);
34878                 var selectedLineString = selectedSequenceKey && _mlyCache.sequences.lineString[selectedSequenceKey];
34879                 var selectedImageKeys = (selectedLineString && selectedLineString.properties.coordinateProperties.image_keys) || [];
34880
34881                 // highlight sibling viewfields on either the selected or the hovered sequences
34882                 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
34883
34884                 context.container().selectAll('.layer-mapillary .viewfield-group')
34885                     .classed('highlighted', function(d) { return highlightedImageKeys.indexOf(d.key) !== -1; })
34886                     .classed('hovered', function(d) { return d.key === hoveredImageKey; })
34887                     .classed('currentView', function(d) { return d.key === selectedImageKey; });
34888
34889                 context.container().selectAll('.layer-mapillary .sequence')
34890                     .classed('highlighted', function(d) { return d.properties.key === hoveredSequenceKey; })
34891                     .classed('currentView', function(d) { return d.properties.key === selectedSequenceKey; });
34892
34893                 // update viewfields if needed
34894                 context.container().selectAll('.viewfield-group .viewfield')
34895                     .attr('d', viewfieldPath);
34896
34897                 function viewfieldPath() {
34898                     var d = this.parentNode.__data__;
34899                     if (d.pano && d.key !== selectedImageKey) {
34900                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
34901                     } else {
34902                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
34903                     }
34904                 }
34905
34906                 return this;
34907             },
34908
34909
34910             updateDetections: function(d) {
34911                 if (!_mlyViewer || _mlyFallback) return;
34912
34913                 var imageKey = d && d.key;
34914                 if (!imageKey) return;
34915
34916                 var detections = _mlyCache.image_detections.forImageKey[imageKey] || [];
34917                 detections.forEach(function(data) {
34918                     var tag = makeTag(data);
34919                     if (tag) {
34920                         var tagComponent = _mlyViewer.getComponent('tag');
34921                         tagComponent.add([tag]);
34922                     }
34923                 });
34924
34925                 function makeTag(data) {
34926                     var valueParts = data.value.split('--');
34927                     if (valueParts.length !== 3) return;
34928
34929                     var text = valueParts[1].replace(/-/g, ' ');
34930                     var tag;
34931
34932                     // Currently only two shapes <Polygon|Point>
34933                     if (data.shape.type === 'Polygon') {
34934                         var polygonGeometry = new Mapillary
34935                             .TagComponent
34936                             .PolygonGeometry(data.shape.coordinates[0]);
34937
34938                         tag = new Mapillary.TagComponent.OutlineTag(
34939                             data.key,
34940                             polygonGeometry,
34941                             {
34942                                 text: text,
34943                                 textColor: 0xffff00,
34944                                 lineColor: 0xffff00,
34945                                 lineWidth: 2,
34946                                 fillColor: 0xffff00,
34947                                 fillOpacity: 0.3,
34948                             }
34949                         );
34950
34951                     } else if (data.shape.type === 'Point') {
34952                         var pointGeometry = new Mapillary
34953                             .TagComponent
34954                             .PointGeometry(data.shape.coordinates[0]);
34955
34956                         tag = new Mapillary.TagComponent.SpotTag(
34957                             data.key,
34958                             pointGeometry,
34959                             {
34960                                 text: text,
34961                                 color: 0xffff00,
34962                                 textColor: 0xffff00
34963                             }
34964                         );
34965                     }
34966
34967                     return tag;
34968                 }
34969             },
34970
34971
34972             cache: function() {
34973                 return _mlyCache;
34974             }
34975
34976         };
34977
34978         function validationIssue(attrs) {
34979             this.type = attrs.type;                // required - name of rule that created the issue (e.g. 'missing_tag')
34980             this.subtype = attrs.subtype;          // optional - category of the issue within the type (e.g. 'relation_type' under 'missing_tag')
34981             this.severity = attrs.severity;        // required - 'warning' or 'error'
34982             this.message = attrs.message;          // required - function returning localized string
34983             this.reference = attrs.reference;      // optional - function(selection) to render reference information
34984             this.entityIds = attrs.entityIds;      // optional - array of IDs of entities involved in the issue
34985             this.loc = attrs.loc;                  // optional - [lon, lat] to zoom in on to see the issue
34986             this.data = attrs.data;                // optional - object containing extra data for the fixes
34987             this.dynamicFixes = attrs.dynamicFixes;// optional - function(context) returning fixes
34988             this.hash = attrs.hash;                // optional - string to further differentiate the issue
34989
34990             this.id = generateID.apply(this);      // generated - see below
34991             this.autoFix = null;                   // generated - if autofix exists, will be set below
34992
34993             // A unique, deterministic string hash.
34994             // Issues with identical id values are considered identical.
34995             function generateID() {
34996                 var parts = [this.type];
34997
34998                 if (this.hash) {   // subclasses can pass in their own differentiator
34999                     parts.push(this.hash);
35000                 }
35001
35002                 if (this.subtype) {
35003                     parts.push(this.subtype);
35004                 }
35005
35006                 // include the entities this issue is for
35007                 // (sort them so the id is deterministic)
35008                 if (this.entityIds) {
35009                     var entityKeys = this.entityIds.slice().sort();
35010                     parts.push.apply(parts, entityKeys);
35011                 }
35012
35013                 return parts.join(':');
35014             }
35015
35016             this.extent = function(resolver) {
35017                 if (this.loc) {
35018                     return geoExtent(this.loc);
35019                 }
35020                 if (this.entityIds && this.entityIds.length) {
35021                     return this.entityIds.reduce(function(extent, entityId) {
35022                         return extent.extend(resolver.entity(entityId).extent(resolver));
35023                     }, geoExtent());
35024                 }
35025                 return null;
35026             };
35027
35028             this.fixes = function(context) {
35029                 var fixes = this.dynamicFixes ? this.dynamicFixes(context) : [];
35030                 var issue = this;
35031
35032                 if (issue.severity === 'warning') {
35033                     // allow ignoring any issue that's not an error
35034                     fixes.push(new validationIssueFix({
35035                         title: _t('issues.fix.ignore_issue.title'),
35036                         icon: 'iD-icon-close',
35037                         onClick: function() {
35038                             context.validator().ignoreIssue(this.issue.id);
35039                         }
35040                     }));
35041                 }
35042
35043                 fixes.forEach(function(fix) {
35044                     fix.id = fix.title;
35045                     // add a reference to the issue for use in actions
35046                     fix.issue = issue;
35047                     if (fix.autoArgs) {
35048                         issue.autoFix = fix;
35049                     }
35050                 });
35051                 return fixes;
35052             };
35053
35054         }
35055
35056
35057         function validationIssueFix(attrs) {
35058             this.title = attrs.title;                   // Required
35059             this.onClick = attrs.onClick;               // Optional - the function to run to apply the fix
35060             this.disabledReason = attrs.disabledReason; // Optional - a string explaining why the fix is unavailable, if any
35061             this.icon = attrs.icon;                     // Optional - shows 'iD-icon-wrench' if not set
35062             this.entityIds = attrs.entityIds || [];     // Optional - used for hover-higlighting.
35063             this.autoArgs = attrs.autoArgs;             // Optional - pass [actions, annotation] arglist if this fix can automatically run
35064
35065             this.issue = null;    // Generated link - added by validationIssue
35066         }
35067
35068         var buildRuleChecks = function() {
35069             return {
35070                 equals: function (equals) {
35071                     return function(tags) {
35072                         return Object.keys(equals).every(function(k) {
35073                             return equals[k] === tags[k];
35074                         });
35075                     };
35076                 },
35077                 notEquals: function (notEquals) {
35078                     return function(tags) {
35079                         return Object.keys(notEquals).some(function(k) {
35080                             return notEquals[k] !== tags[k];
35081                         });
35082                     };
35083                 },
35084                 absence: function(absence) {
35085                     return function(tags) {
35086                         return Object.keys(tags).indexOf(absence) === -1;
35087                     };
35088                 },
35089                 presence: function(presence) {
35090                     return function(tags) {
35091                         return Object.keys(tags).indexOf(presence) > -1;
35092                     };
35093                 },
35094                 greaterThan: function(greaterThan) {
35095                     var key = Object.keys(greaterThan)[0];
35096                     var value = greaterThan[key];
35097
35098                     return function(tags) {
35099                         return tags[key] > value;
35100                     };
35101                 },
35102                 greaterThanEqual: function(greaterThanEqual) {
35103                     var key = Object.keys(greaterThanEqual)[0];
35104                     var value = greaterThanEqual[key];
35105
35106                     return function(tags) {
35107                         return tags[key] >= value;
35108                     };
35109                 },
35110                 lessThan: function(lessThan) {
35111                     var key = Object.keys(lessThan)[0];
35112                     var value = lessThan[key];
35113
35114                     return function(tags) {
35115                         return tags[key] < value;
35116                     };
35117                 },
35118                 lessThanEqual: function(lessThanEqual) {
35119                     var key = Object.keys(lessThanEqual)[0];
35120                     var value = lessThanEqual[key];
35121
35122                     return function(tags) {
35123                         return tags[key] <= value;
35124                     };
35125                 },
35126                 positiveRegex: function(positiveRegex) {
35127                     var tagKey = Object.keys(positiveRegex)[0];
35128                     var expression = positiveRegex[tagKey].join('|');
35129                     var regex = new RegExp(expression);
35130
35131                     return function(tags) {
35132                         return regex.test(tags[tagKey]);
35133                     };
35134                 },
35135                 negativeRegex: function(negativeRegex) {
35136                     var tagKey = Object.keys(negativeRegex)[0];
35137                     var expression = negativeRegex[tagKey].join('|');
35138                     var regex = new RegExp(expression);
35139
35140                     return function(tags) {
35141                         return !regex.test(tags[tagKey]);
35142                     };
35143                 }
35144             };
35145         };
35146
35147         var buildLineKeys = function() {
35148             return {
35149                 highway: {
35150                     rest_area: true,
35151                     services: true
35152                 },
35153                 railway: {
35154                     roundhouse: true,
35155                     station: true,
35156                     traverser: true,
35157                     turntable: true,
35158                     wash: true
35159                 }
35160             };
35161         };
35162
35163         var serviceMapRules = {
35164             init: function() {
35165                 this._ruleChecks  = buildRuleChecks();
35166                 this._validationRules = [];
35167                 this._areaKeys = osmAreaKeys;
35168                 this._lineKeys = buildLineKeys();
35169             },
35170
35171             // list of rules only relevant to tag checks...
35172             filterRuleChecks: function(selector) {
35173                 var _ruleChecks = this._ruleChecks;
35174                 return Object.keys(selector).reduce(function(rules, key) {
35175                     if (['geometry', 'error', 'warning'].indexOf(key) === -1) {
35176                         rules.push(_ruleChecks[key](selector[key]));
35177                     }
35178                     return rules;
35179                 }, []);
35180             },
35181
35182             // builds tagMap from mapcss-parse selector object...
35183             buildTagMap: function(selector) {
35184                 var getRegexValues = function(regexes) {
35185                     return regexes.map(function(regex) {
35186                         return regex.replace(/\$|\^/g, '');
35187                     });
35188                 };
35189
35190                 var tagMap = Object.keys(selector).reduce(function (expectedTags, key) {
35191                     var values;
35192                     var isRegex = /regex/gi.test(key);
35193                     var isEqual = /equals/gi.test(key);
35194
35195                     if (isRegex || isEqual) {
35196                         Object.keys(selector[key]).forEach(function(selectorKey) {
35197                             values = isEqual ? [selector[key][selectorKey]] : getRegexValues(selector[key][selectorKey]);
35198
35199                             if (expectedTags.hasOwnProperty(selectorKey)) {
35200                                 values = values.concat(expectedTags[selectorKey]);
35201                             }
35202
35203                             expectedTags[selectorKey] = values;
35204                         });
35205
35206                     } else if (/(greater|less)Than(Equal)?|presence/g.test(key)) {
35207                         var tagKey = /presence/.test(key) ? selector[key] : Object.keys(selector[key])[0];
35208
35209                         values = [selector[key][tagKey]];
35210
35211                         if (expectedTags.hasOwnProperty(tagKey)) {
35212                             values = values.concat(expectedTags[tagKey]);
35213                         }
35214
35215                         expectedTags[tagKey] = values;
35216                     }
35217
35218                     return expectedTags;
35219                 }, {});
35220
35221                 return tagMap;
35222             },
35223
35224             // inspired by osmWay#isArea()
35225             inferGeometry: function(tagMap) {
35226                 var _lineKeys = this._lineKeys;
35227                 var _areaKeys = this._areaKeys;
35228
35229                 var keyValueDoesNotImplyArea = function(key) {
35230                     return utilArrayIntersection(tagMap[key], Object.keys(_areaKeys[key])).length > 0;
35231                 };
35232                 var keyValueImpliesLine = function(key) {
35233                     return utilArrayIntersection(tagMap[key], Object.keys(_lineKeys[key])).length > 0;
35234                 };
35235
35236                 if (tagMap.hasOwnProperty('area')) {
35237                     if (tagMap.area.indexOf('yes') > -1) {
35238                         return 'area';
35239                     }
35240                     if (tagMap.area.indexOf('no') > -1) {
35241                         return 'line';
35242                     }
35243                 }
35244
35245                 for (var key in tagMap) {
35246                     if (key in _areaKeys && !keyValueDoesNotImplyArea(key)) {
35247                         return 'area';
35248                     }
35249                     if (key in _lineKeys && keyValueImpliesLine(key)) {
35250                         return 'area';
35251                     }
35252                 }
35253
35254                 return 'line';
35255             },
35256
35257             // adds from mapcss-parse selector check...
35258             addRule: function(selector) {
35259                 var rule = {
35260                     // checks relevant to mapcss-selector
35261                     checks: this.filterRuleChecks(selector),
35262                     // true if all conditions for a tag error are true..
35263                     matches: function(entity) {
35264                         return this.checks.every(function(check) {
35265                             return check(entity.tags);
35266                         });
35267                     },
35268                     // borrowed from Way#isArea()
35269                     inferredGeometry: this.inferGeometry(this.buildTagMap(selector), this._areaKeys),
35270                     geometryMatches: function(entity, graph) {
35271                         if (entity.type === 'node' || entity.type === 'relation') {
35272                             return selector.geometry === entity.type;
35273                         } else if (entity.type === 'way') {
35274                             return this.inferredGeometry === entity.geometry(graph);
35275                         }
35276                     },
35277                     // when geometries match and tag matches are present, return a warning...
35278                     findIssues: function (entity, graph, issues) {
35279                         if (this.geometryMatches(entity, graph) && this.matches(entity)) {
35280                             var severity = Object.keys(selector).indexOf('error') > -1
35281                                     ? 'error'
35282                                     : 'warning';
35283                             var message = selector[severity];
35284                             issues.push(new validationIssue({
35285                                 type: 'maprules',
35286                                 severity: severity,
35287                                 message: function() {
35288                                     return message;
35289                                 },
35290                                 entityIds: [entity.id]
35291                             }));
35292                         }
35293                     }
35294                 };
35295                 this._validationRules.push(rule);
35296             },
35297
35298             clearRules: function() { this._validationRules = []; },
35299
35300             // returns validationRules...
35301             validationRules: function() { return this._validationRules; },
35302
35303             // returns ruleChecks
35304             ruleChecks: function() { return this._ruleChecks; }
35305         };
35306
35307         var apibase$1 = 'https://nominatim.openstreetmap.org/';
35308         var _inflight = {};
35309         var _nominatimCache;
35310
35311
35312         var serviceNominatim = {
35313
35314             init: function() {
35315                 _inflight = {};
35316                 _nominatimCache = new RBush();
35317             },
35318
35319             reset: function() {
35320                 Object.values(_inflight).forEach(function(controller) { controller.abort(); });
35321                 _inflight = {};
35322                 _nominatimCache = new RBush();
35323             },
35324
35325
35326             countryCode: function (location, callback) {
35327                 this.reverse(location, function(err, result) {
35328                     if (err) {
35329                         return callback(err);
35330                     } else if (result.address) {
35331                         return callback(null, result.address.country_code);
35332                     } else {
35333                         return callback('Unable to geocode', null);
35334                     }
35335                 });
35336             },
35337
35338
35339             reverse: function (loc, callback) {
35340                 var cached = _nominatimCache.search(
35341                     { minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1] }
35342                 );
35343
35344                 if (cached.length > 0) {
35345                     if (callback) callback(null, cached[0].data);
35346                     return;
35347                 }
35348
35349                 var params = { zoom: 13, format: 'json', addressdetails: 1, lat: loc[1], lon: loc[0] };
35350                 var url = apibase$1 + 'reverse?' + utilQsString(params);
35351
35352                 if (_inflight[url]) return;
35353                 var controller = new AbortController();
35354                 _inflight[url] = controller;
35355
35356                 d3_json(url, { signal: controller.signal })
35357                     .then(function(result) {
35358                         delete _inflight[url];
35359                         if (result && result.error) {
35360                             throw new Error(result.error);
35361                         }
35362                         var extent = geoExtent(loc).padByMeters(200);
35363                         _nominatimCache.insert(Object.assign(extent.bbox(), {data: result}));
35364                         if (callback) callback(null, result);
35365                     })
35366                     .catch(function(err) {
35367                         delete _inflight[url];
35368                         if (err.name === 'AbortError') return;
35369                         if (callback) callback(err.message);
35370                     });
35371             },
35372
35373
35374             search: function (val, callback) {
35375                 var searchVal = encodeURIComponent(val);
35376                 var url = apibase$1 + 'search/' + searchVal + '?limit=10&format=json';
35377
35378                 if (_inflight[url]) return;
35379                 var controller = new AbortController();
35380                 _inflight[url] = controller;
35381
35382                 d3_json(url, { signal: controller.signal })
35383                     .then(function(result) {
35384                         delete _inflight[url];
35385                         if (result && result.error) {
35386                             throw new Error(result.error);
35387                         }
35388                         if (callback) callback(null, result);
35389                     })
35390                     .catch(function(err) {
35391                         delete _inflight[url];
35392                         if (err.name === 'AbortError') return;
35393                         if (callback) callback(err.message);
35394                     });
35395             }
35396
35397         };
35398
35399         var apibase$2 = 'https://openstreetcam.org';
35400         var maxResults$1 = 1000;
35401         var tileZoom$1 = 14;
35402         var tiler$4 = utilTiler().zoomExtent([tileZoom$1, tileZoom$1]).skipNullIsland(true);
35403         var dispatch$5 = dispatch('loadedImages');
35404         var imgZoom = d3_zoom()
35405             .extent([[0, 0], [320, 240]])
35406             .translateExtent([[0, 0], [320, 240]])
35407             .scaleExtent([1, 15]);
35408         var _oscCache;
35409         var _oscSelectedImage;
35410
35411
35412         function abortRequest$4(controller) {
35413             controller.abort();
35414         }
35415
35416
35417         function maxPageAtZoom$1(z) {
35418             if (z < 15)   return 2;
35419             if (z === 15) return 5;
35420             if (z === 16) return 10;
35421             if (z === 17) return 20;
35422             if (z === 18) return 40;
35423             if (z > 18)   return 80;
35424         }
35425
35426
35427         function loadTiles$1(which, url, projection) {
35428             var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
35429             var tiles = tiler$4.getTiles(projection);
35430
35431             // abort inflight requests that are no longer needed
35432             var cache = _oscCache[which];
35433             Object.keys(cache.inflight).forEach(function(k) {
35434                 var wanted = tiles.find(function(tile) { return k.indexOf(tile.id + ',') === 0; });
35435                 if (!wanted) {
35436                     abortRequest$4(cache.inflight[k]);
35437                     delete cache.inflight[k];
35438                 }
35439             });
35440
35441             tiles.forEach(function(tile) {
35442                 loadNextTilePage$1(which, currZoom, url, tile);
35443             });
35444         }
35445
35446
35447         function loadNextTilePage$1(which, currZoom, url, tile) {
35448             var cache = _oscCache[which];
35449             var bbox = tile.extent.bbox();
35450             var maxPages = maxPageAtZoom$1(currZoom);
35451             var nextPage = cache.nextPage[tile.id] || 1;
35452             var params = utilQsString({
35453                 ipp: maxResults$1,
35454                 page: nextPage,
35455                 // client_id: clientId,
35456                 bbTopLeft: [bbox.maxY, bbox.minX].join(','),
35457                 bbBottomRight: [bbox.minY, bbox.maxX].join(',')
35458             }, true);
35459
35460             if (nextPage > maxPages) return;
35461
35462             var id = tile.id + ',' + String(nextPage);
35463             if (cache.loaded[id] || cache.inflight[id]) return;
35464
35465             var controller = new AbortController();
35466             cache.inflight[id] = controller;
35467
35468             var options = {
35469                 method: 'POST',
35470                 signal: controller.signal,
35471                 body: params,
35472                 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
35473             };
35474
35475             d3_json(url, options)
35476                 .then(function(data) {
35477                     cache.loaded[id] = true;
35478                     delete cache.inflight[id];
35479                     if (!data || !data.currentPageItems || !data.currentPageItems.length) {
35480                         throw new Error('No Data');
35481                     }
35482
35483                     var features = data.currentPageItems.map(function(item) {
35484                         var loc = [+item.lng, +item.lat];
35485                         var d;
35486
35487                         if (which === 'images') {
35488                             d = {
35489                                 loc: loc,
35490                                 key: item.id,
35491                                 ca: +item.heading,
35492                                 captured_at: (item.shot_date || item.date_added),
35493                                 captured_by: item.username,
35494                                 imagePath: item.lth_name,
35495                                 sequence_id: item.sequence_id,
35496                                 sequence_index: +item.sequence_index
35497                             };
35498
35499                             // cache sequence info
35500                             var seq = _oscCache.sequences[d.sequence_id];
35501                             if (!seq) {
35502                                 seq = { rotation: 0, images: [] };
35503                                 _oscCache.sequences[d.sequence_id] = seq;
35504                             }
35505                             seq.images[d.sequence_index] = d;
35506                         }
35507
35508                         return {
35509                             minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
35510                         };
35511                     });
35512
35513                     cache.rtree.load(features);
35514
35515                     if (data.currentPageItems.length === maxResults$1) {  // more pages to load
35516                         cache.nextPage[tile.id] = nextPage + 1;
35517                         loadNextTilePage$1(which, currZoom, url, tile);
35518                     } else {
35519                         cache.nextPage[tile.id] = Infinity;     // no more pages to load
35520                     }
35521
35522                     if (which === 'images') {
35523                         dispatch$5.call('loadedImages');
35524                     }
35525                 })
35526                 .catch(function() {
35527                     cache.loaded[id] = true;
35528                     delete cache.inflight[id];
35529                 });
35530         }
35531
35532
35533         // partition viewport into higher zoom tiles
35534         function partitionViewport$1(projection) {
35535             var z = geoScaleToZoom(projection.scale());
35536             var z2 = (Math.ceil(z * 2) / 2) + 2.5;   // round to next 0.5 and add 2.5
35537             var tiler = utilTiler().zoomExtent([z2, z2]);
35538
35539             return tiler.getTiles(projection)
35540                 .map(function(tile) { return tile.extent; });
35541         }
35542
35543
35544         // no more than `limit` results per partition.
35545         function searchLimited$1(limit, projection, rtree) {
35546             limit = limit || 5;
35547
35548             return partitionViewport$1(projection)
35549                 .reduce(function(result, extent) {
35550                     var found = rtree.search(extent.bbox())
35551                         .slice(0, limit)
35552                         .map(function(d) { return d.data; });
35553
35554                     return (found.length ? result.concat(found) : result);
35555                 }, []);
35556         }
35557
35558
35559         var serviceOpenstreetcam = {
35560
35561             init: function() {
35562                 if (!_oscCache) {
35563                     this.reset();
35564                 }
35565
35566                 this.event = utilRebind(this, dispatch$5, 'on');
35567             },
35568
35569             reset: function() {
35570                 if (_oscCache) {
35571                     Object.values(_oscCache.images.inflight).forEach(abortRequest$4);
35572                 }
35573
35574                 _oscCache = {
35575                     images: { inflight: {}, loaded: {}, nextPage: {}, rtree: new RBush() },
35576                     sequences: {}
35577                 };
35578
35579                 _oscSelectedImage = null;
35580             },
35581
35582
35583             images: function(projection) {
35584                 var limit = 5;
35585                 return searchLimited$1(limit, projection, _oscCache.images.rtree);
35586             },
35587
35588
35589             sequences: function(projection) {
35590                 var viewport = projection.clipExtent();
35591                 var min = [viewport[0][0], viewport[1][1]];
35592                 var max = [viewport[1][0], viewport[0][1]];
35593                 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
35594                 var sequenceKeys = {};
35595
35596                 // all sequences for images in viewport
35597                 _oscCache.images.rtree.search(bbox)
35598                     .forEach(function(d) { sequenceKeys[d.data.sequence_id] = true; });
35599
35600                 // make linestrings from those sequences
35601                 var lineStrings = [];
35602                 Object.keys(sequenceKeys)
35603                     .forEach(function(sequenceKey) {
35604                         var seq = _oscCache.sequences[sequenceKey];
35605                         var images = seq && seq.images;
35606                         if (images) {
35607                             lineStrings.push({
35608                                 type: 'LineString',
35609                                 coordinates: images.map(function (d) { return d.loc; }).filter(Boolean),
35610                                 properties: { key: sequenceKey }
35611                             });
35612                         }
35613                     });
35614                 return lineStrings;
35615             },
35616
35617
35618             loadImages: function(projection) {
35619                 var url = apibase$2 + '/1.0/list/nearby-photos/';
35620                 loadTiles$1('images', url, projection);
35621             },
35622
35623
35624             loadViewer: function(context) {
35625                 var that = this;
35626
35627                 // add osc-wrapper
35628                 var wrap = context.container().select('.photoviewer').selectAll('.osc-wrapper')
35629                     .data([0]);
35630
35631                 var wrapEnter = wrap.enter()
35632                     .append('div')
35633                     .attr('class', 'photo-wrapper osc-wrapper')
35634                     .classed('hide', true)
35635                     .call(imgZoom.on('zoom', zoomPan))
35636                     .on('dblclick.zoom', null);
35637
35638                 wrapEnter
35639                     .append('div')
35640                     .attr('class', 'photo-attribution fillD');
35641
35642                 var controlsEnter = wrapEnter
35643                     .append('div')
35644                     .attr('class', 'photo-controls-wrap')
35645                     .append('div')
35646                     .attr('class', 'photo-controls');
35647
35648                 controlsEnter
35649                     .append('button')
35650                     .on('click.back', step(-1))
35651                     .text('◄');
35652
35653                 controlsEnter
35654                     .append('button')
35655                     .on('click.rotate-ccw', rotate(-90))
35656                     .text('⤿');
35657
35658                 controlsEnter
35659                     .append('button')
35660                     .on('click.rotate-cw', rotate(90))
35661                     .text('⤾');
35662
35663                 controlsEnter
35664                     .append('button')
35665                     .on('click.forward', step(1))
35666                     .text('►');
35667
35668                 wrapEnter
35669                     .append('div')
35670                     .attr('class', 'osc-image-wrap');
35671
35672
35673                 // Register viewer resize handler
35674                 context.ui().photoviewer.on('resize.openstreetcam', function(dimensions) {
35675                     imgZoom = d3_zoom()
35676                         .extent([[0, 0], dimensions])
35677                         .translateExtent([[0, 0], dimensions])
35678                         .scaleExtent([1, 15])
35679                         .on('zoom', zoomPan);
35680                 });
35681
35682
35683                 function zoomPan() {
35684                     var t = event.transform;
35685                     context.container().select('.photoviewer .osc-image-wrap')
35686                         .call(utilSetTransform, t.x, t.y, t.k);
35687                 }
35688
35689
35690                 function rotate(deg) {
35691                     return function() {
35692                         if (!_oscSelectedImage) return;
35693                         var sequenceKey = _oscSelectedImage.sequence_id;
35694                         var sequence = _oscCache.sequences[sequenceKey];
35695                         if (!sequence) return;
35696
35697                         var r = sequence.rotation || 0;
35698                         r += deg;
35699
35700                         if (r > 180) r -= 360;
35701                         if (r < -180) r += 360;
35702                         sequence.rotation = r;
35703
35704                         var wrap = context.container().select('.photoviewer .osc-wrapper');
35705
35706                         wrap
35707                             .transition()
35708                             .duration(100)
35709                             .call(imgZoom.transform, identity$2);
35710
35711                         wrap.selectAll('.osc-image')
35712                             .transition()
35713                             .duration(100)
35714                             .style('transform', 'rotate(' + r + 'deg)');
35715                     };
35716                 }
35717
35718                 function step(stepBy) {
35719                     return function() {
35720                         if (!_oscSelectedImage) return;
35721                         var sequenceKey = _oscSelectedImage.sequence_id;
35722                         var sequence = _oscCache.sequences[sequenceKey];
35723                         if (!sequence) return;
35724
35725                         var nextIndex = _oscSelectedImage.sequence_index + stepBy;
35726                         var nextImage = sequence.images[nextIndex];
35727                         if (!nextImage) return;
35728
35729                         context.map().centerEase(nextImage.loc);
35730
35731                         that
35732                             .selectImage(context, nextImage)
35733                             .updateViewer(context, nextImage);
35734                     };
35735                 }
35736             },
35737
35738
35739             showViewer: function(context) {
35740                 var viewer = context.container().select('.photoviewer')
35741                     .classed('hide', false);
35742
35743                 var isHidden = viewer.selectAll('.photo-wrapper.osc-wrapper.hide').size();
35744
35745                 if (isHidden) {
35746                     viewer
35747                         .selectAll('.photo-wrapper:not(.osc-wrapper)')
35748                         .classed('hide', true);
35749
35750                     viewer
35751                         .selectAll('.photo-wrapper.osc-wrapper')
35752                         .classed('hide', false);
35753                 }
35754
35755                 return this;
35756             },
35757
35758
35759             hideViewer: function(context) {
35760                 _oscSelectedImage = null;
35761
35762                 var viewer = context.container().select('.photoviewer');
35763                 if (!viewer.empty()) viewer.datum(null);
35764
35765                 viewer
35766                     .classed('hide', true)
35767                     .selectAll('.photo-wrapper')
35768                     .classed('hide', true);
35769
35770                 context.container().selectAll('.viewfield-group, .sequence, .icon-sign')
35771                     .classed('currentView', false);
35772
35773                 return this.setStyles(context, null, true);
35774             },
35775
35776
35777             updateViewer: function(context, d) {
35778                 var wrap = context.container().select('.photoviewer .osc-wrapper');
35779                 var imageWrap = wrap.selectAll('.osc-image-wrap');
35780                 var attribution = wrap.selectAll('.photo-attribution').html('');
35781
35782                 wrap
35783                     .transition()
35784                     .duration(100)
35785                     .call(imgZoom.transform, identity$2);
35786
35787                 imageWrap
35788                     .selectAll('.osc-image')
35789                     .remove();
35790
35791                 if (d) {
35792                     var sequence = _oscCache.sequences[d.sequence_id];
35793                     var r = (sequence && sequence.rotation) || 0;
35794
35795                     imageWrap
35796                         .append('img')
35797                         .attr('class', 'osc-image')
35798                         .attr('src', apibase$2 + '/' + d.imagePath)
35799                         .style('transform', 'rotate(' + r + 'deg)');
35800
35801                     if (d.captured_by) {
35802                         attribution
35803                             .append('a')
35804                             .attr('class', 'captured_by')
35805                             .attr('target', '_blank')
35806                             .attr('href', 'https://openstreetcam.org/user/' + encodeURIComponent(d.captured_by))
35807                             .text('@' + d.captured_by);
35808
35809                         attribution
35810                             .append('span')
35811                             .text('|');
35812                     }
35813
35814                     if (d.captured_at) {
35815                         attribution
35816                             .append('span')
35817                             .attr('class', 'captured_at')
35818                             .text(localeDateString(d.captured_at));
35819
35820                         attribution
35821                             .append('span')
35822                             .text('|');
35823                     }
35824
35825                     attribution
35826                         .append('a')
35827                         .attr('class', 'image-link')
35828                         .attr('target', '_blank')
35829                         .attr('href', 'https://openstreetcam.org/details/' + d.sequence_id + '/' + d.sequence_index)
35830                         .text('openstreetcam.org');
35831                 }
35832
35833                 return this;
35834
35835
35836                 function localeDateString(s) {
35837                     if (!s) return null;
35838                     var options = { day: 'numeric', month: 'short', year: 'numeric' };
35839                     var d = new Date(s);
35840                     if (isNaN(d.getTime())) return null;
35841                     return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
35842                 }
35843             },
35844
35845
35846             selectImage: function(context, d) {
35847                 _oscSelectedImage = d;
35848                 var viewer = context.container().select('.photoviewer');
35849                 if (!viewer.empty()) viewer.datum(d);
35850
35851                 this.setStyles(context, null, true);
35852
35853                 context.container().selectAll('.icon-sign')
35854                     .classed('currentView', false);
35855
35856                 return this;
35857             },
35858
35859
35860             getSelectedImage: function() {
35861                 return _oscSelectedImage;
35862             },
35863
35864
35865             getSequenceKeyForImage: function(d) {
35866                 return d && d.sequence_id;
35867             },
35868
35869
35870             // Updates the currently highlighted sequence and selected bubble.
35871             // Reset is only necessary when interacting with the viewport because
35872             // this implicitly changes the currently selected bubble/sequence
35873             setStyles: function(context, hovered, reset) {
35874                 if (reset) {  // reset all layers
35875                     context.container().selectAll('.viewfield-group')
35876                         .classed('highlighted', false)
35877                         .classed('hovered', false)
35878                         .classed('currentView', false);
35879
35880                     context.container().selectAll('.sequence')
35881                         .classed('highlighted', false)
35882                         .classed('currentView', false);
35883                 }
35884
35885                 var hoveredImageKey = hovered && hovered.key;
35886                 var hoveredSequenceKey = this.getSequenceKeyForImage(hovered);
35887                 var hoveredSequence = hoveredSequenceKey && _oscCache.sequences[hoveredSequenceKey];
35888                 var hoveredImageKeys = (hoveredSequence && hoveredSequence.images.map(function (d) { return d.key; })) || [];
35889
35890                 var viewer = context.container().select('.photoviewer');
35891                 var selected = viewer.empty() ? undefined : viewer.datum();
35892                 var selectedImageKey = selected && selected.key;
35893                 var selectedSequenceKey = this.getSequenceKeyForImage(selected);
35894                 var selectedSequence = selectedSequenceKey && _oscCache.sequences[selectedSequenceKey];
35895                 var selectedImageKeys = (selectedSequence && selectedSequence.images.map(function (d) { return d.key; })) || [];
35896
35897                 // highlight sibling viewfields on either the selected or the hovered sequences
35898                 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
35899
35900                 context.container().selectAll('.layer-openstreetcam .viewfield-group')
35901                     .classed('highlighted', function(d) { return highlightedImageKeys.indexOf(d.key) !== -1; })
35902                     .classed('hovered', function(d) { return d.key === hoveredImageKey; })
35903                     .classed('currentView', function(d) { return d.key === selectedImageKey; });
35904
35905                 context.container().selectAll('.layer-openstreetcam .sequence')
35906                     .classed('highlighted', function(d) { return d.properties.key === hoveredSequenceKey; })
35907                     .classed('currentView', function(d) { return d.properties.key === selectedSequenceKey; });
35908
35909                 // update viewfields if needed
35910                 context.container().selectAll('.viewfield-group .viewfield')
35911                     .attr('d', viewfieldPath);
35912
35913                 function viewfieldPath() {
35914                     var d = this.parentNode.__data__;
35915                     if (d.pano && d.key !== selectedImageKey) {
35916                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
35917                     } else {
35918                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
35919                     }
35920                 }
35921
35922                 return this;
35923             },
35924
35925
35926             cache: function() {
35927                 return _oscCache;
35928             }
35929
35930         };
35931
35932         /**
35933          * Checks if `value` is the
35934          * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
35935          * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
35936          *
35937          * @static
35938          * @memberOf _
35939          * @since 0.1.0
35940          * @category Lang
35941          * @param {*} value The value to check.
35942          * @returns {boolean} Returns `true` if `value` is an object, else `false`.
35943          * @example
35944          *
35945          * _.isObject({});
35946          * // => true
35947          *
35948          * _.isObject([1, 2, 3]);
35949          * // => true
35950          *
35951          * _.isObject(_.noop);
35952          * // => true
35953          *
35954          * _.isObject(null);
35955          * // => false
35956          */
35957         function isObject$1(value) {
35958           var type = typeof value;
35959           return value != null && (type == 'object' || type == 'function');
35960         }
35961
35962         /** Detect free variable `global` from Node.js. */
35963         var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
35964
35965         /** Detect free variable `self`. */
35966         var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
35967
35968         /** Used as a reference to the global object. */
35969         var root$2 = freeGlobal || freeSelf || Function('return this')();
35970
35971         /**
35972          * Gets the timestamp of the number of milliseconds that have elapsed since
35973          * the Unix epoch (1 January 1970 00:00:00 UTC).
35974          *
35975          * @static
35976          * @memberOf _
35977          * @since 2.4.0
35978          * @category Date
35979          * @returns {number} Returns the timestamp.
35980          * @example
35981          *
35982          * _.defer(function(stamp) {
35983          *   console.log(_.now() - stamp);
35984          * }, _.now());
35985          * // => Logs the number of milliseconds it took for the deferred invocation.
35986          */
35987         var now$1 = function() {
35988           return root$2.Date.now();
35989         };
35990
35991         /** Built-in value references. */
35992         var Symbol$1 = root$2.Symbol;
35993
35994         /** Used for built-in method references. */
35995         var objectProto = Object.prototype;
35996
35997         /** Used to check objects for own properties. */
35998         var hasOwnProperty$2 = objectProto.hasOwnProperty;
35999
36000         /**
36001          * Used to resolve the
36002          * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
36003          * of values.
36004          */
36005         var nativeObjectToString = objectProto.toString;
36006
36007         /** Built-in value references. */
36008         var symToStringTag = Symbol$1 ? Symbol$1.toStringTag : undefined;
36009
36010         /**
36011          * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
36012          *
36013          * @private
36014          * @param {*} value The value to query.
36015          * @returns {string} Returns the raw `toStringTag`.
36016          */
36017         function getRawTag(value) {
36018           var isOwn = hasOwnProperty$2.call(value, symToStringTag),
36019               tag = value[symToStringTag];
36020
36021           try {
36022             value[symToStringTag] = undefined;
36023             var unmasked = true;
36024           } catch (e) {}
36025
36026           var result = nativeObjectToString.call(value);
36027           if (unmasked) {
36028             if (isOwn) {
36029               value[symToStringTag] = tag;
36030             } else {
36031               delete value[symToStringTag];
36032             }
36033           }
36034           return result;
36035         }
36036
36037         /** Used for built-in method references. */
36038         var objectProto$1 = Object.prototype;
36039
36040         /**
36041          * Used to resolve the
36042          * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
36043          * of values.
36044          */
36045         var nativeObjectToString$1 = objectProto$1.toString;
36046
36047         /**
36048          * Converts `value` to a string using `Object.prototype.toString`.
36049          *
36050          * @private
36051          * @param {*} value The value to convert.
36052          * @returns {string} Returns the converted string.
36053          */
36054         function objectToString$2(value) {
36055           return nativeObjectToString$1.call(value);
36056         }
36057
36058         /** `Object#toString` result references. */
36059         var nullTag = '[object Null]',
36060             undefinedTag = '[object Undefined]';
36061
36062         /** Built-in value references. */
36063         var symToStringTag$1 = Symbol$1 ? Symbol$1.toStringTag : undefined;
36064
36065         /**
36066          * The base implementation of `getTag` without fallbacks for buggy environments.
36067          *
36068          * @private
36069          * @param {*} value The value to query.
36070          * @returns {string} Returns the `toStringTag`.
36071          */
36072         function baseGetTag(value) {
36073           if (value == null) {
36074             return value === undefined ? undefinedTag : nullTag;
36075           }
36076           return (symToStringTag$1 && symToStringTag$1 in Object(value))
36077             ? getRawTag(value)
36078             : objectToString$2(value);
36079         }
36080
36081         /**
36082          * Checks if `value` is object-like. A value is object-like if it's not `null`
36083          * and has a `typeof` result of "object".
36084          *
36085          * @static
36086          * @memberOf _
36087          * @since 4.0.0
36088          * @category Lang
36089          * @param {*} value The value to check.
36090          * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
36091          * @example
36092          *
36093          * _.isObjectLike({});
36094          * // => true
36095          *
36096          * _.isObjectLike([1, 2, 3]);
36097          * // => true
36098          *
36099          * _.isObjectLike(_.noop);
36100          * // => false
36101          *
36102          * _.isObjectLike(null);
36103          * // => false
36104          */
36105         function isObjectLike(value) {
36106           return value != null && typeof value == 'object';
36107         }
36108
36109         /** `Object#toString` result references. */
36110         var symbolTag = '[object Symbol]';
36111
36112         /**
36113          * Checks if `value` is classified as a `Symbol` primitive or object.
36114          *
36115          * @static
36116          * @memberOf _
36117          * @since 4.0.0
36118          * @category Lang
36119          * @param {*} value The value to check.
36120          * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
36121          * @example
36122          *
36123          * _.isSymbol(Symbol.iterator);
36124          * // => true
36125          *
36126          * _.isSymbol('abc');
36127          * // => false
36128          */
36129         function isSymbol$4(value) {
36130           return typeof value == 'symbol' ||
36131             (isObjectLike(value) && baseGetTag(value) == symbolTag);
36132         }
36133
36134         /** Used as references for various `Number` constants. */
36135         var NAN = 0 / 0;
36136
36137         /** Used to match leading and trailing whitespace. */
36138         var reTrim = /^\s+|\s+$/g;
36139
36140         /** Used to detect bad signed hexadecimal string values. */
36141         var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
36142
36143         /** Used to detect binary string values. */
36144         var reIsBinary = /^0b[01]+$/i;
36145
36146         /** Used to detect octal string values. */
36147         var reIsOctal = /^0o[0-7]+$/i;
36148
36149         /** Built-in method references without a dependency on `root`. */
36150         var freeParseInt = parseInt;
36151
36152         /**
36153          * Converts `value` to a number.
36154          *
36155          * @static
36156          * @memberOf _
36157          * @since 4.0.0
36158          * @category Lang
36159          * @param {*} value The value to process.
36160          * @returns {number} Returns the number.
36161          * @example
36162          *
36163          * _.toNumber(3.2);
36164          * // => 3.2
36165          *
36166          * _.toNumber(Number.MIN_VALUE);
36167          * // => 5e-324
36168          *
36169          * _.toNumber(Infinity);
36170          * // => Infinity
36171          *
36172          * _.toNumber('3.2');
36173          * // => 3.2
36174          */
36175         function toNumber(value) {
36176           if (typeof value == 'number') {
36177             return value;
36178           }
36179           if (isSymbol$4(value)) {
36180             return NAN;
36181           }
36182           if (isObject$1(value)) {
36183             var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
36184             value = isObject$1(other) ? (other + '') : other;
36185           }
36186           if (typeof value != 'string') {
36187             return value === 0 ? value : +value;
36188           }
36189           value = value.replace(reTrim, '');
36190           var isBinary = reIsBinary.test(value);
36191           return (isBinary || reIsOctal.test(value))
36192             ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
36193             : (reIsBadHex.test(value) ? NAN : +value);
36194         }
36195
36196         /** Error message constants. */
36197         var FUNC_ERROR_TEXT = 'Expected a function';
36198
36199         /* Built-in method references for those with the same name as other `lodash` methods. */
36200         var nativeMax = Math.max,
36201             nativeMin = Math.min;
36202
36203         /**
36204          * Creates a debounced function that delays invoking `func` until after `wait`
36205          * milliseconds have elapsed since the last time the debounced function was
36206          * invoked. The debounced function comes with a `cancel` method to cancel
36207          * delayed `func` invocations and a `flush` method to immediately invoke them.
36208          * Provide `options` to indicate whether `func` should be invoked on the
36209          * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
36210          * with the last arguments provided to the debounced function. Subsequent
36211          * calls to the debounced function return the result of the last `func`
36212          * invocation.
36213          *
36214          * **Note:** If `leading` and `trailing` options are `true`, `func` is
36215          * invoked on the trailing edge of the timeout only if the debounced function
36216          * is invoked more than once during the `wait` timeout.
36217          *
36218          * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
36219          * until to the next tick, similar to `setTimeout` with a timeout of `0`.
36220          *
36221          * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
36222          * for details over the differences between `_.debounce` and `_.throttle`.
36223          *
36224          * @static
36225          * @memberOf _
36226          * @since 0.1.0
36227          * @category Function
36228          * @param {Function} func The function to debounce.
36229          * @param {number} [wait=0] The number of milliseconds to delay.
36230          * @param {Object} [options={}] The options object.
36231          * @param {boolean} [options.leading=false]
36232          *  Specify invoking on the leading edge of the timeout.
36233          * @param {number} [options.maxWait]
36234          *  The maximum time `func` is allowed to be delayed before it's invoked.
36235          * @param {boolean} [options.trailing=true]
36236          *  Specify invoking on the trailing edge of the timeout.
36237          * @returns {Function} Returns the new debounced function.
36238          * @example
36239          *
36240          * // Avoid costly calculations while the window size is in flux.
36241          * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
36242          *
36243          * // Invoke `sendMail` when clicked, debouncing subsequent calls.
36244          * jQuery(element).on('click', _.debounce(sendMail, 300, {
36245          *   'leading': true,
36246          *   'trailing': false
36247          * }));
36248          *
36249          * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
36250          * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
36251          * var source = new EventSource('/stream');
36252          * jQuery(source).on('message', debounced);
36253          *
36254          * // Cancel the trailing debounced invocation.
36255          * jQuery(window).on('popstate', debounced.cancel);
36256          */
36257         function debounce(func, wait, options) {
36258           var lastArgs,
36259               lastThis,
36260               maxWait,
36261               result,
36262               timerId,
36263               lastCallTime,
36264               lastInvokeTime = 0,
36265               leading = false,
36266               maxing = false,
36267               trailing = true;
36268
36269           if (typeof func != 'function') {
36270             throw new TypeError(FUNC_ERROR_TEXT);
36271           }
36272           wait = toNumber(wait) || 0;
36273           if (isObject$1(options)) {
36274             leading = !!options.leading;
36275             maxing = 'maxWait' in options;
36276             maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
36277             trailing = 'trailing' in options ? !!options.trailing : trailing;
36278           }
36279
36280           function invokeFunc(time) {
36281             var args = lastArgs,
36282                 thisArg = lastThis;
36283
36284             lastArgs = lastThis = undefined;
36285             lastInvokeTime = time;
36286             result = func.apply(thisArg, args);
36287             return result;
36288           }
36289
36290           function leadingEdge(time) {
36291             // Reset any `maxWait` timer.
36292             lastInvokeTime = time;
36293             // Start the timer for the trailing edge.
36294             timerId = setTimeout(timerExpired, wait);
36295             // Invoke the leading edge.
36296             return leading ? invokeFunc(time) : result;
36297           }
36298
36299           function remainingWait(time) {
36300             var timeSinceLastCall = time - lastCallTime,
36301                 timeSinceLastInvoke = time - lastInvokeTime,
36302                 timeWaiting = wait - timeSinceLastCall;
36303
36304             return maxing
36305               ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
36306               : timeWaiting;
36307           }
36308
36309           function shouldInvoke(time) {
36310             var timeSinceLastCall = time - lastCallTime,
36311                 timeSinceLastInvoke = time - lastInvokeTime;
36312
36313             // Either this is the first call, activity has stopped and we're at the
36314             // trailing edge, the system time has gone backwards and we're treating
36315             // it as the trailing edge, or we've hit the `maxWait` limit.
36316             return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
36317               (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
36318           }
36319
36320           function timerExpired() {
36321             var time = now$1();
36322             if (shouldInvoke(time)) {
36323               return trailingEdge(time);
36324             }
36325             // Restart the timer.
36326             timerId = setTimeout(timerExpired, remainingWait(time));
36327           }
36328
36329           function trailingEdge(time) {
36330             timerId = undefined;
36331
36332             // Only invoke if we have `lastArgs` which means `func` has been
36333             // debounced at least once.
36334             if (trailing && lastArgs) {
36335               return invokeFunc(time);
36336             }
36337             lastArgs = lastThis = undefined;
36338             return result;
36339           }
36340
36341           function cancel() {
36342             if (timerId !== undefined) {
36343               clearTimeout(timerId);
36344             }
36345             lastInvokeTime = 0;
36346             lastArgs = lastCallTime = lastThis = timerId = undefined;
36347           }
36348
36349           function flush() {
36350             return timerId === undefined ? result : trailingEdge(now$1());
36351           }
36352
36353           function debounced() {
36354             var time = now$1(),
36355                 isInvoking = shouldInvoke(time);
36356
36357             lastArgs = arguments;
36358             lastThis = this;
36359             lastCallTime = time;
36360
36361             if (isInvoking) {
36362               if (timerId === undefined) {
36363                 return leadingEdge(lastCallTime);
36364               }
36365               if (maxing) {
36366                 // Handle invocations in a tight loop.
36367                 clearTimeout(timerId);
36368                 timerId = setTimeout(timerExpired, wait);
36369                 return invokeFunc(lastCallTime);
36370               }
36371             }
36372             if (timerId === undefined) {
36373               timerId = setTimeout(timerExpired, wait);
36374             }
36375             return result;
36376           }
36377           debounced.cancel = cancel;
36378           debounced.flush = flush;
36379           return debounced;
36380         }
36381
36382         /** Error message constants. */
36383         var FUNC_ERROR_TEXT$1 = 'Expected a function';
36384
36385         /**
36386          * Creates a throttled function that only invokes `func` at most once per
36387          * every `wait` milliseconds. The throttled function comes with a `cancel`
36388          * method to cancel delayed `func` invocations and a `flush` method to
36389          * immediately invoke them. Provide `options` to indicate whether `func`
36390          * should be invoked on the leading and/or trailing edge of the `wait`
36391          * timeout. The `func` is invoked with the last arguments provided to the
36392          * throttled function. Subsequent calls to the throttled function return the
36393          * result of the last `func` invocation.
36394          *
36395          * **Note:** If `leading` and `trailing` options are `true`, `func` is
36396          * invoked on the trailing edge of the timeout only if the throttled function
36397          * is invoked more than once during the `wait` timeout.
36398          *
36399          * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
36400          * until to the next tick, similar to `setTimeout` with a timeout of `0`.
36401          *
36402          * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
36403          * for details over the differences between `_.throttle` and `_.debounce`.
36404          *
36405          * @static
36406          * @memberOf _
36407          * @since 0.1.0
36408          * @category Function
36409          * @param {Function} func The function to throttle.
36410          * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
36411          * @param {Object} [options={}] The options object.
36412          * @param {boolean} [options.leading=true]
36413          *  Specify invoking on the leading edge of the timeout.
36414          * @param {boolean} [options.trailing=true]
36415          *  Specify invoking on the trailing edge of the timeout.
36416          * @returns {Function} Returns the new throttled function.
36417          * @example
36418          *
36419          * // Avoid excessively updating the position while scrolling.
36420          * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
36421          *
36422          * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
36423          * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
36424          * jQuery(element).on('click', throttled);
36425          *
36426          * // Cancel the trailing throttled invocation.
36427          * jQuery(window).on('popstate', throttled.cancel);
36428          */
36429         function throttle(func, wait, options) {
36430           var leading = true,
36431               trailing = true;
36432
36433           if (typeof func != 'function') {
36434             throw new TypeError(FUNC_ERROR_TEXT$1);
36435           }
36436           if (isObject$1(options)) {
36437             leading = 'leading' in options ? !!options.leading : leading;
36438             trailing = 'trailing' in options ? !!options.trailing : trailing;
36439           }
36440           return debounce(func, wait, {
36441             'leading': leading,
36442             'maxWait': wait,
36443             'trailing': trailing
36444           });
36445         }
36446
36447         var hashes = createCommonjsModule(function (module, exports) {
36448         /**
36449          * jshashes - https://github.com/h2non/jshashes
36450          * Released under the "New BSD" license
36451          *
36452          * Algorithms specification:
36453          *
36454          * MD5 - http://www.ietf.org/rfc/rfc1321.txt
36455          * RIPEMD-160 - http://homes.esat.kuleuven.be/~bosselae/ripemd160.html
36456          * SHA1   - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
36457          * SHA256 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
36458          * SHA512 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
36459          * HMAC - http://www.ietf.org/rfc/rfc2104.txt
36460          */
36461         (function() {
36462           var Hashes;
36463
36464           function utf8Encode(str) {
36465             var x, y, output = '',
36466               i = -1,
36467               l;
36468
36469             if (str && str.length) {
36470               l = str.length;
36471               while ((i += 1) < l) {
36472                 /* Decode utf-16 surrogate pairs */
36473                 x = str.charCodeAt(i);
36474                 y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
36475                 if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
36476                   x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
36477                   i += 1;
36478                 }
36479                 /* Encode output as utf-8 */
36480                 if (x <= 0x7F) {
36481                   output += String.fromCharCode(x);
36482                 } else if (x <= 0x7FF) {
36483                   output += String.fromCharCode(0xC0 | ((x >>> 6) & 0x1F),
36484                     0x80 | (x & 0x3F));
36485                 } else if (x <= 0xFFFF) {
36486                   output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
36487                     0x80 | ((x >>> 6) & 0x3F),
36488                     0x80 | (x & 0x3F));
36489                 } else if (x <= 0x1FFFFF) {
36490                   output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
36491                     0x80 | ((x >>> 12) & 0x3F),
36492                     0x80 | ((x >>> 6) & 0x3F),
36493                     0x80 | (x & 0x3F));
36494                 }
36495               }
36496             }
36497             return output;
36498           }
36499
36500           function utf8Decode(str) {
36501             var i, ac, c1, c2, c3, arr = [],
36502               l;
36503             i = ac = c1 = c2 = c3 = 0;
36504
36505             if (str && str.length) {
36506               l = str.length;
36507               str += '';
36508
36509               while (i < l) {
36510                 c1 = str.charCodeAt(i);
36511                 ac += 1;
36512                 if (c1 < 128) {
36513                   arr[ac] = String.fromCharCode(c1);
36514                   i += 1;
36515                 } else if (c1 > 191 && c1 < 224) {
36516                   c2 = str.charCodeAt(i + 1);
36517                   arr[ac] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
36518                   i += 2;
36519                 } else {
36520                   c2 = str.charCodeAt(i + 1);
36521                   c3 = str.charCodeAt(i + 2);
36522                   arr[ac] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
36523                   i += 3;
36524                 }
36525               }
36526             }
36527             return arr.join('');
36528           }
36529
36530           /**
36531            * Add integers, wrapping at 2^32. This uses 16-bit operations internally
36532            * to work around bugs in some JS interpreters.
36533            */
36534
36535           function safe_add(x, y) {
36536             var lsw = (x & 0xFFFF) + (y & 0xFFFF),
36537               msw = (x >> 16) + (y >> 16) + (lsw >> 16);
36538             return (msw << 16) | (lsw & 0xFFFF);
36539           }
36540
36541           /**
36542            * Bitwise rotate a 32-bit number to the left.
36543            */
36544
36545           function bit_rol(num, cnt) {
36546             return (num << cnt) | (num >>> (32 - cnt));
36547           }
36548
36549           /**
36550            * Convert a raw string to a hex string
36551            */
36552
36553           function rstr2hex(input, hexcase) {
36554             var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
36555               output = '',
36556               x, i = 0,
36557               l = input.length;
36558             for (; i < l; i += 1) {
36559               x = input.charCodeAt(i);
36560               output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
36561             }
36562             return output;
36563           }
36564
36565           /**
36566            * Convert an array of big-endian words to a string
36567            */
36568
36569           function binb2rstr(input) {
36570             var i, l = input.length * 32,
36571               output = '';
36572             for (i = 0; i < l; i += 8) {
36573               output += String.fromCharCode((input[i >> 5] >>> (24 - i % 32)) & 0xFF);
36574             }
36575             return output;
36576           }
36577
36578           /**
36579            * Convert an array of little-endian words to a string
36580            */
36581
36582           function binl2rstr(input) {
36583             var i, l = input.length * 32,
36584               output = '';
36585             for (i = 0; i < l; i += 8) {
36586               output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
36587             }
36588             return output;
36589           }
36590
36591           /**
36592            * Convert a raw string to an array of little-endian words
36593            * Characters >255 have their high-byte silently ignored.
36594            */
36595
36596           function rstr2binl(input) {
36597             var i, l = input.length * 8,
36598               output = Array(input.length >> 2),
36599               lo = output.length;
36600             for (i = 0; i < lo; i += 1) {
36601               output[i] = 0;
36602             }
36603             for (i = 0; i < l; i += 8) {
36604               output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);
36605             }
36606             return output;
36607           }
36608
36609           /**
36610            * Convert a raw string to an array of big-endian words
36611            * Characters >255 have their high-byte silently ignored.
36612            */
36613
36614           function rstr2binb(input) {
36615             var i, l = input.length * 8,
36616               output = Array(input.length >> 2),
36617               lo = output.length;
36618             for (i = 0; i < lo; i += 1) {
36619               output[i] = 0;
36620             }
36621             for (i = 0; i < l; i += 8) {
36622               output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
36623             }
36624             return output;
36625           }
36626
36627           /**
36628            * Convert a raw string to an arbitrary string encoding
36629            */
36630
36631           function rstr2any(input, encoding) {
36632             var divisor = encoding.length,
36633               remainders = Array(),
36634               i, q, x, ld, quotient, dividend, output, full_length;
36635
36636             /* Convert to an array of 16-bit big-endian values, forming the dividend */
36637             dividend = Array(Math.ceil(input.length / 2));
36638             ld = dividend.length;
36639             for (i = 0; i < ld; i += 1) {
36640               dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
36641             }
36642
36643             /**
36644              * Repeatedly perform a long division. The binary array forms the dividend,
36645              * the length of the encoding is the divisor. Once computed, the quotient
36646              * forms the dividend for the next step. We stop when the dividend is zerHashes.
36647              * All remainders are stored for later use.
36648              */
36649             while (dividend.length > 0) {
36650               quotient = Array();
36651               x = 0;
36652               for (i = 0; i < dividend.length; i += 1) {
36653                 x = (x << 16) + dividend[i];
36654                 q = Math.floor(x / divisor);
36655                 x -= q * divisor;
36656                 if (quotient.length > 0 || q > 0) {
36657                   quotient[quotient.length] = q;
36658                 }
36659               }
36660               remainders[remainders.length] = x;
36661               dividend = quotient;
36662             }
36663
36664             /* Convert the remainders to the output string */
36665             output = '';
36666             for (i = remainders.length - 1; i >= 0; i--) {
36667               output += encoding.charAt(remainders[i]);
36668             }
36669
36670             /* Append leading zero equivalents */
36671             full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
36672             for (i = output.length; i < full_length; i += 1) {
36673               output = encoding[0] + output;
36674             }
36675             return output;
36676           }
36677
36678           /**
36679            * Convert a raw string to a base-64 string
36680            */
36681
36682           function rstr2b64(input, b64pad) {
36683             var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
36684               output = '',
36685               len = input.length,
36686               i, j, triplet;
36687             b64pad = b64pad || '=';
36688             for (i = 0; i < len; i += 3) {
36689               triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
36690               for (j = 0; j < 4; j += 1) {
36691                 if (i * 8 + j * 6 > input.length * 8) {
36692                   output += b64pad;
36693                 } else {
36694                   output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F);
36695                 }
36696               }
36697             }
36698             return output;
36699           }
36700
36701           Hashes = {
36702             /**
36703              * @property {String} version
36704              * @readonly
36705              */
36706             VERSION: '1.0.6',
36707             /**
36708              * @member Hashes
36709              * @class Base64
36710              * @constructor
36711              */
36712             Base64: function() {
36713               // private properties
36714               var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
36715                 pad = '=', // default pad according with the RFC standard
36716                 utf8 = true; // by default enable UTF-8 support encoding
36717
36718               // public method for encoding
36719               this.encode = function(input) {
36720                 var i, j, triplet,
36721                   output = '',
36722                   len = input.length;
36723
36724                 pad = pad || '=';
36725                 input = (utf8) ? utf8Encode(input) : input;
36726
36727                 for (i = 0; i < len; i += 3) {
36728                   triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
36729                   for (j = 0; j < 4; j += 1) {
36730                     if (i * 8 + j * 6 > len * 8) {
36731                       output += pad;
36732                     } else {
36733                       output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F);
36734                     }
36735                   }
36736                 }
36737                 return output;
36738               };
36739
36740               // public method for decoding
36741               this.decode = function(input) {
36742                 // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
36743                 var i, o1, o2, o3, h1, h2, h3, h4, bits, ac,
36744                   dec = '',
36745                   arr = [];
36746                 if (!input) {
36747                   return input;
36748                 }
36749
36750                 i = ac = 0;
36751                 input = input.replace(new RegExp('\\' + pad, 'gi'), ''); // use '='
36752                 //input += '';
36753
36754                 do { // unpack four hexets into three octets using index points in b64
36755                   h1 = tab.indexOf(input.charAt(i += 1));
36756                   h2 = tab.indexOf(input.charAt(i += 1));
36757                   h3 = tab.indexOf(input.charAt(i += 1));
36758                   h4 = tab.indexOf(input.charAt(i += 1));
36759
36760                   bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
36761
36762                   o1 = bits >> 16 & 0xff;
36763                   o2 = bits >> 8 & 0xff;
36764                   o3 = bits & 0xff;
36765                   ac += 1;
36766
36767                   if (h3 === 64) {
36768                     arr[ac] = String.fromCharCode(o1);
36769                   } else if (h4 === 64) {
36770                     arr[ac] = String.fromCharCode(o1, o2);
36771                   } else {
36772                     arr[ac] = String.fromCharCode(o1, o2, o3);
36773                   }
36774                 } while (i < input.length);
36775
36776                 dec = arr.join('');
36777                 dec = (utf8) ? utf8Decode(dec) : dec;
36778
36779                 return dec;
36780               };
36781
36782               // set custom pad string
36783               this.setPad = function(str) {
36784                 pad = str || pad;
36785                 return this;
36786               };
36787               // set custom tab string characters
36788               this.setTab = function(str) {
36789                 tab = str || tab;
36790                 return this;
36791               };
36792               this.setUTF8 = function(bool) {
36793                 if (typeof bool === 'boolean') {
36794                   utf8 = bool;
36795                 }
36796                 return this;
36797               };
36798             },
36799
36800             /**
36801              * CRC-32 calculation
36802              * @member Hashes
36803              * @method CRC32
36804              * @static
36805              * @param {String} str Input String
36806              * @return {String}
36807              */
36808             CRC32: function(str) {
36809               var crc = 0,
36810                 x = 0,
36811                 y = 0,
36812                 table, i, iTop;
36813               str = utf8Encode(str);
36814
36815               table = [
36816                 '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ',
36817                 '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ',
36818                 '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ',
36819                 '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ',
36820                 'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ',
36821                 '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ',
36822                 'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ',
36823                 '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ',
36824                 'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ',
36825                 '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ',
36826                 'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ',
36827                 '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ',
36828                 'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ',
36829                 '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ',
36830                 '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ',
36831                 '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ',
36832                 '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ',
36833                 'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ',
36834                 '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ',
36835                 'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ',
36836                 '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ',
36837                 'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ',
36838                 '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ',
36839                 'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ',
36840                 '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ',
36841                 'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'
36842               ].join('');
36843
36844               crc = crc ^ (-1);
36845               for (i = 0, iTop = str.length; i < iTop; i += 1) {
36846                 y = (crc ^ str.charCodeAt(i)) & 0xFF;
36847                 x = '0x' + table.substr(y * 9, 8);
36848                 crc = (crc >>> 8) ^ x;
36849               }
36850               // always return a positive number (that's what >>> 0 does)
36851               return (crc ^ (-1)) >>> 0;
36852             },
36853             /**
36854              * @member Hashes
36855              * @class MD5
36856              * @constructor
36857              * @param {Object} [config]
36858              *
36859              * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
36860              * Digest Algorithm, as defined in RFC 1321.
36861              * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
36862              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
36863              * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
36864              */
36865             MD5: function(options) {
36866               /**
36867                * Private config properties. You may need to tweak these to be compatible with
36868                * the server-side, but the defaults work in most cases.
36869                * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
36870                */
36871               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
36872                 b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
36873                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
36874
36875               // privileged (public) methods
36876               this.hex = function(s) {
36877                 return rstr2hex(rstr(s), hexcase);
36878               };
36879               this.b64 = function(s) {
36880                 return rstr2b64(rstr(s), b64pad);
36881               };
36882               this.any = function(s, e) {
36883                 return rstr2any(rstr(s), e);
36884               };
36885               this.raw = function(s) {
36886                 return rstr(s);
36887               };
36888               this.hex_hmac = function(k, d) {
36889                 return rstr2hex(rstr_hmac(k, d), hexcase);
36890               };
36891               this.b64_hmac = function(k, d) {
36892                 return rstr2b64(rstr_hmac(k, d), b64pad);
36893               };
36894               this.any_hmac = function(k, d, e) {
36895                 return rstr2any(rstr_hmac(k, d), e);
36896               };
36897               /**
36898                * Perform a simple self-test to see if the VM is working
36899                * @return {String} Hexadecimal hash sample
36900                */
36901               this.vm_test = function() {
36902                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
36903               };
36904               /**
36905                * Enable/disable uppercase hexadecimal returned string
36906                * @param {Boolean}
36907                * @return {Object} this
36908                */
36909               this.setUpperCase = function(a) {
36910                 if (typeof a === 'boolean') {
36911                   hexcase = a;
36912                 }
36913                 return this;
36914               };
36915               /**
36916                * Defines a base64 pad string
36917                * @param {String} Pad
36918                * @return {Object} this
36919                */
36920               this.setPad = function(a) {
36921                 b64pad = a || b64pad;
36922                 return this;
36923               };
36924               /**
36925                * Defines a base64 pad string
36926                * @param {Boolean}
36927                * @return {Object} [this]
36928                */
36929               this.setUTF8 = function(a) {
36930                 if (typeof a === 'boolean') {
36931                   utf8 = a;
36932                 }
36933                 return this;
36934               };
36935
36936               // private methods
36937
36938               /**
36939                * Calculate the MD5 of a raw string
36940                */
36941
36942               function rstr(s) {
36943                 s = (utf8) ? utf8Encode(s) : s;
36944                 return binl2rstr(binl(rstr2binl(s), s.length * 8));
36945               }
36946
36947               /**
36948                * Calculate the HMAC-MD5, of a key and some data (raw strings)
36949                */
36950
36951               function rstr_hmac(key, data) {
36952                 var bkey, ipad, opad, hash, i;
36953
36954                 key = (utf8) ? utf8Encode(key) : key;
36955                 data = (utf8) ? utf8Encode(data) : data;
36956                 bkey = rstr2binl(key);
36957                 if (bkey.length > 16) {
36958                   bkey = binl(bkey, key.length * 8);
36959                 }
36960
36961                 ipad = Array(16), opad = Array(16);
36962                 for (i = 0; i < 16; i += 1) {
36963                   ipad[i] = bkey[i] ^ 0x36363636;
36964                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
36965                 }
36966                 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
36967                 return binl2rstr(binl(opad.concat(hash), 512 + 128));
36968               }
36969
36970               /**
36971                * Calculate the MD5 of an array of little-endian words, and a bit length.
36972                */
36973
36974               function binl(x, len) {
36975                 var i, olda, oldb, oldc, oldd,
36976                   a = 1732584193,
36977                   b = -271733879,
36978                   c = -1732584194,
36979                   d = 271733878;
36980
36981                 /* append padding */
36982                 x[len >> 5] |= 0x80 << ((len) % 32);
36983                 x[(((len + 64) >>> 9) << 4) + 14] = len;
36984
36985                 for (i = 0; i < x.length; i += 16) {
36986                   olda = a;
36987                   oldb = b;
36988                   oldc = c;
36989                   oldd = d;
36990
36991                   a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
36992                   d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
36993                   c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
36994                   b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
36995                   a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
36996                   d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
36997                   c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
36998                   b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
36999                   a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
37000                   d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
37001                   c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
37002                   b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
37003                   a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
37004                   d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
37005                   c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
37006                   b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
37007
37008                   a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
37009                   d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
37010                   c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
37011                   b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
37012                   a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
37013                   d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
37014                   c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
37015                   b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
37016                   a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
37017                   d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
37018                   c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
37019                   b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
37020                   a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
37021                   d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
37022                   c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
37023                   b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
37024
37025                   a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
37026                   d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
37027                   c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
37028                   b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
37029                   a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
37030                   d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
37031                   c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
37032                   b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
37033                   a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
37034                   d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
37035                   c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
37036                   b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
37037                   a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
37038                   d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
37039                   c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
37040                   b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
37041
37042                   a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
37043                   d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
37044                   c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
37045                   b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
37046                   a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
37047                   d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
37048                   c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
37049                   b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
37050                   a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
37051                   d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
37052                   c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
37053                   b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
37054                   a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
37055                   d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
37056                   c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
37057                   b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
37058
37059                   a = safe_add(a, olda);
37060                   b = safe_add(b, oldb);
37061                   c = safe_add(c, oldc);
37062                   d = safe_add(d, oldd);
37063                 }
37064                 return Array(a, b, c, d);
37065               }
37066
37067               /**
37068                * These functions implement the four basic operations the algorithm uses.
37069                */
37070
37071               function md5_cmn(q, a, b, x, s, t) {
37072                 return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
37073               }
37074
37075               function md5_ff(a, b, c, d, x, s, t) {
37076                 return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
37077               }
37078
37079               function md5_gg(a, b, c, d, x, s, t) {
37080                 return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
37081               }
37082
37083               function md5_hh(a, b, c, d, x, s, t) {
37084                 return md5_cmn(b ^ c ^ d, a, b, x, s, t);
37085               }
37086
37087               function md5_ii(a, b, c, d, x, s, t) {
37088                 return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
37089               }
37090             },
37091             /**
37092              * @member Hashes
37093              * @class Hashes.SHA1
37094              * @param {Object} [config]
37095              * @constructor
37096              *
37097              * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
37098              * Version 2.2 Copyright Paul Johnston 2000 - 2009.
37099              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
37100              * See http://pajhome.org.uk/crypt/md5 for details.
37101              */
37102             SHA1: function(options) {
37103               /**
37104                * Private config properties. You may need to tweak these to be compatible with
37105                * the server-side, but the defaults work in most cases.
37106                * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
37107                */
37108               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
37109                 b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
37110                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
37111
37112               // public methods
37113               this.hex = function(s) {
37114                 return rstr2hex(rstr(s), hexcase);
37115               };
37116               this.b64 = function(s) {
37117                 return rstr2b64(rstr(s), b64pad);
37118               };
37119               this.any = function(s, e) {
37120                 return rstr2any(rstr(s), e);
37121               };
37122               this.raw = function(s) {
37123                 return rstr(s);
37124               };
37125               this.hex_hmac = function(k, d) {
37126                 return rstr2hex(rstr_hmac(k, d));
37127               };
37128               this.b64_hmac = function(k, d) {
37129                 return rstr2b64(rstr_hmac(k, d), b64pad);
37130               };
37131               this.any_hmac = function(k, d, e) {
37132                 return rstr2any(rstr_hmac(k, d), e);
37133               };
37134               /**
37135                * Perform a simple self-test to see if the VM is working
37136                * @return {String} Hexadecimal hash sample
37137                * @public
37138                */
37139               this.vm_test = function() {
37140                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
37141               };
37142               /**
37143                * @description Enable/disable uppercase hexadecimal returned string
37144                * @param {boolean}
37145                * @return {Object} this
37146                * @public
37147                */
37148               this.setUpperCase = function(a) {
37149                 if (typeof a === 'boolean') {
37150                   hexcase = a;
37151                 }
37152                 return this;
37153               };
37154               /**
37155                * @description Defines a base64 pad string
37156                * @param {string} Pad
37157                * @return {Object} this
37158                * @public
37159                */
37160               this.setPad = function(a) {
37161                 b64pad = a || b64pad;
37162                 return this;
37163               };
37164               /**
37165                * @description Defines a base64 pad string
37166                * @param {boolean}
37167                * @return {Object} this
37168                * @public
37169                */
37170               this.setUTF8 = function(a) {
37171                 if (typeof a === 'boolean') {
37172                   utf8 = a;
37173                 }
37174                 return this;
37175               };
37176
37177               // private methods
37178
37179               /**
37180                * Calculate the SHA-512 of a raw string
37181                */
37182
37183               function rstr(s) {
37184                 s = (utf8) ? utf8Encode(s) : s;
37185                 return binb2rstr(binb(rstr2binb(s), s.length * 8));
37186               }
37187
37188               /**
37189                * Calculate the HMAC-SHA1 of a key and some data (raw strings)
37190                */
37191
37192               function rstr_hmac(key, data) {
37193                 var bkey, ipad, opad, i, hash;
37194                 key = (utf8) ? utf8Encode(key) : key;
37195                 data = (utf8) ? utf8Encode(data) : data;
37196                 bkey = rstr2binb(key);
37197
37198                 if (bkey.length > 16) {
37199                   bkey = binb(bkey, key.length * 8);
37200                 }
37201                 ipad = Array(16), opad = Array(16);
37202                 for (i = 0; i < 16; i += 1) {
37203                   ipad[i] = bkey[i] ^ 0x36363636;
37204                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
37205                 }
37206                 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
37207                 return binb2rstr(binb(opad.concat(hash), 512 + 160));
37208               }
37209
37210               /**
37211                * Calculate the SHA-1 of an array of big-endian words, and a bit length
37212                */
37213
37214               function binb(x, len) {
37215                 var i, j, t, olda, oldb, oldc, oldd, olde,
37216                   w = Array(80),
37217                   a = 1732584193,
37218                   b = -271733879,
37219                   c = -1732584194,
37220                   d = 271733878,
37221                   e = -1009589776;
37222
37223                 /* append padding */
37224                 x[len >> 5] |= 0x80 << (24 - len % 32);
37225                 x[((len + 64 >> 9) << 4) + 15] = len;
37226
37227                 for (i = 0; i < x.length; i += 16) {
37228                   olda = a;
37229                   oldb = b;
37230                   oldc = c;
37231                   oldd = d;
37232                   olde = e;
37233
37234                   for (j = 0; j < 80; j += 1) {
37235                     if (j < 16) {
37236                       w[j] = x[i + j];
37237                     } else {
37238                       w[j] = bit_rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
37239                     }
37240                     t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
37241                       safe_add(safe_add(e, w[j]), sha1_kt(j)));
37242                     e = d;
37243                     d = c;
37244                     c = bit_rol(b, 30);
37245                     b = a;
37246                     a = t;
37247                   }
37248
37249                   a = safe_add(a, olda);
37250                   b = safe_add(b, oldb);
37251                   c = safe_add(c, oldc);
37252                   d = safe_add(d, oldd);
37253                   e = safe_add(e, olde);
37254                 }
37255                 return Array(a, b, c, d, e);
37256               }
37257
37258               /**
37259                * Perform the appropriate triplet combination function for the current
37260                * iteration
37261                */
37262
37263               function sha1_ft(t, b, c, d) {
37264                 if (t < 20) {
37265                   return (b & c) | ((~b) & d);
37266                 }
37267                 if (t < 40) {
37268                   return b ^ c ^ d;
37269                 }
37270                 if (t < 60) {
37271                   return (b & c) | (b & d) | (c & d);
37272                 }
37273                 return b ^ c ^ d;
37274               }
37275
37276               /**
37277                * Determine the appropriate additive constant for the current iteration
37278                */
37279
37280               function sha1_kt(t) {
37281                 return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
37282                   (t < 60) ? -1894007588 : -899497514;
37283               }
37284             },
37285             /**
37286              * @class Hashes.SHA256
37287              * @param {config}
37288              *
37289              * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
37290              * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
37291              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
37292              * See http://pajhome.org.uk/crypt/md5 for details.
37293              * Also http://anmar.eu.org/projects/jssha2/
37294              */
37295             SHA256: function(options) {
37296               /**
37297                * Private properties configuration variables. You may need to tweak these to be compatible with
37298                * the server-side, but the defaults work in most cases.
37299                * @see this.setUpperCase() method
37300                * @see this.setPad() method
37301                */
37302               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase  */
37303                 b64pad = (options && typeof options.pad === 'string') ? options.pad : '=',
37304                 /* base-64 pad character. Default '=' for strict RFC compliance   */
37305                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true,
37306                 /* enable/disable utf8 encoding */
37307                 sha256_K;
37308
37309               /* privileged (public) methods */
37310               this.hex = function(s) {
37311                 return rstr2hex(rstr(s, utf8));
37312               };
37313               this.b64 = function(s) {
37314                 return rstr2b64(rstr(s, utf8), b64pad);
37315               };
37316               this.any = function(s, e) {
37317                 return rstr2any(rstr(s, utf8), e);
37318               };
37319               this.raw = function(s) {
37320                 return rstr(s, utf8);
37321               };
37322               this.hex_hmac = function(k, d) {
37323                 return rstr2hex(rstr_hmac(k, d));
37324               };
37325               this.b64_hmac = function(k, d) {
37326                 return rstr2b64(rstr_hmac(k, d), b64pad);
37327               };
37328               this.any_hmac = function(k, d, e) {
37329                 return rstr2any(rstr_hmac(k, d), e);
37330               };
37331               /**
37332                * Perform a simple self-test to see if the VM is working
37333                * @return {String} Hexadecimal hash sample
37334                * @public
37335                */
37336               this.vm_test = function() {
37337                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
37338               };
37339               /**
37340                * Enable/disable uppercase hexadecimal returned string
37341                * @param {boolean}
37342                * @return {Object} this
37343                * @public
37344                */
37345               this.setUpperCase = function(a) {
37346                 if (typeof a === 'boolean') {
37347                   hexcase = a;
37348                 }
37349                 return this;
37350               };
37351               /**
37352                * @description Defines a base64 pad string
37353                * @param {string} Pad
37354                * @return {Object} this
37355                * @public
37356                */
37357               this.setPad = function(a) {
37358                 b64pad = a || b64pad;
37359                 return this;
37360               };
37361               /**
37362                * Defines a base64 pad string
37363                * @param {boolean}
37364                * @return {Object} this
37365                * @public
37366                */
37367               this.setUTF8 = function(a) {
37368                 if (typeof a === 'boolean') {
37369                   utf8 = a;
37370                 }
37371                 return this;
37372               };
37373
37374               // private methods
37375
37376               /**
37377                * Calculate the SHA-512 of a raw string
37378                */
37379
37380               function rstr(s, utf8) {
37381                 s = (utf8) ? utf8Encode(s) : s;
37382                 return binb2rstr(binb(rstr2binb(s), s.length * 8));
37383               }
37384
37385               /**
37386                * Calculate the HMAC-sha256 of a key and some data (raw strings)
37387                */
37388
37389               function rstr_hmac(key, data) {
37390                 key = (utf8) ? utf8Encode(key) : key;
37391                 data = (utf8) ? utf8Encode(data) : data;
37392                 var hash, i = 0,
37393                   bkey = rstr2binb(key),
37394                   ipad = Array(16),
37395                   opad = Array(16);
37396
37397                 if (bkey.length > 16) {
37398                   bkey = binb(bkey, key.length * 8);
37399                 }
37400
37401                 for (; i < 16; i += 1) {
37402                   ipad[i] = bkey[i] ^ 0x36363636;
37403                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
37404                 }
37405
37406                 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
37407                 return binb2rstr(binb(opad.concat(hash), 512 + 256));
37408               }
37409
37410               /*
37411                * Main sha256 function, with its support functions
37412                */
37413
37414               function sha256_S(X, n) {
37415                 return (X >>> n) | (X << (32 - n));
37416               }
37417
37418               function sha256_R(X, n) {
37419                 return (X >>> n);
37420               }
37421
37422               function sha256_Ch(x, y, z) {
37423                 return ((x & y) ^ ((~x) & z));
37424               }
37425
37426               function sha256_Maj(x, y, z) {
37427                 return ((x & y) ^ (x & z) ^ (y & z));
37428               }
37429
37430               function sha256_Sigma0256(x) {
37431                 return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));
37432               }
37433
37434               function sha256_Sigma1256(x) {
37435                 return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));
37436               }
37437
37438               function sha256_Gamma0256(x) {
37439                 return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));
37440               }
37441
37442               function sha256_Gamma1256(x) {
37443                 return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));
37444               }
37445
37446               sha256_K = [
37447                 1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993, -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
37448                 1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
37449                 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
37450                 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
37451                 1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885, -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
37452                 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
37453                 1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872, -1866530822, -1538233109, -1090935817, -965641998
37454               ];
37455
37456               function binb(m, l) {
37457                 var HASH = [1779033703, -1150833019, 1013904242, -1521486534,
37458                   1359893119, -1694144372, 528734635, 1541459225
37459                 ];
37460                 var W = new Array(64);
37461                 var a, b, c, d, e, f, g, h;
37462                 var i, j, T1, T2;
37463
37464                 /* append padding */
37465                 m[l >> 5] |= 0x80 << (24 - l % 32);
37466                 m[((l + 64 >> 9) << 4) + 15] = l;
37467
37468                 for (i = 0; i < m.length; i += 16) {
37469                   a = HASH[0];
37470                   b = HASH[1];
37471                   c = HASH[2];
37472                   d = HASH[3];
37473                   e = HASH[4];
37474                   f = HASH[5];
37475                   g = HASH[6];
37476                   h = HASH[7];
37477
37478                   for (j = 0; j < 64; j += 1) {
37479                     if (j < 16) {
37480                       W[j] = m[j + i];
37481                     } else {
37482                       W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
37483                         sha256_Gamma0256(W[j - 15])), W[j - 16]);
37484                     }
37485
37486                     T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)),
37487                       sha256_K[j]), W[j]);
37488                     T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
37489                     h = g;
37490                     g = f;
37491                     f = e;
37492                     e = safe_add(d, T1);
37493                     d = c;
37494                     c = b;
37495                     b = a;
37496                     a = safe_add(T1, T2);
37497                   }
37498
37499                   HASH[0] = safe_add(a, HASH[0]);
37500                   HASH[1] = safe_add(b, HASH[1]);
37501                   HASH[2] = safe_add(c, HASH[2]);
37502                   HASH[3] = safe_add(d, HASH[3]);
37503                   HASH[4] = safe_add(e, HASH[4]);
37504                   HASH[5] = safe_add(f, HASH[5]);
37505                   HASH[6] = safe_add(g, HASH[6]);
37506                   HASH[7] = safe_add(h, HASH[7]);
37507                 }
37508                 return HASH;
37509               }
37510
37511             },
37512
37513             /**
37514              * @class Hashes.SHA512
37515              * @param {config}
37516              *
37517              * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
37518              * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
37519              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
37520              * See http://pajhome.org.uk/crypt/md5 for details.
37521              */
37522             SHA512: function(options) {
37523               /**
37524                * Private properties configuration variables. You may need to tweak these to be compatible with
37525                * the server-side, but the defaults work in most cases.
37526                * @see this.setUpperCase() method
37527                * @see this.setPad() method
37528                */
37529               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false,
37530                 /* hexadecimal output case format. false - lowercase; true - uppercase  */
37531                 b64pad = (options && typeof options.pad === 'string') ? options.pad : '=',
37532                 /* base-64 pad character. Default '=' for strict RFC compliance   */
37533                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true,
37534                 /* enable/disable utf8 encoding */
37535                 sha512_k;
37536
37537               /* privileged (public) methods */
37538               this.hex = function(s) {
37539                 return rstr2hex(rstr(s));
37540               };
37541               this.b64 = function(s) {
37542                 return rstr2b64(rstr(s), b64pad);
37543               };
37544               this.any = function(s, e) {
37545                 return rstr2any(rstr(s), e);
37546               };
37547               this.raw = function(s) {
37548                 return rstr(s);
37549               };
37550               this.hex_hmac = function(k, d) {
37551                 return rstr2hex(rstr_hmac(k, d));
37552               };
37553               this.b64_hmac = function(k, d) {
37554                 return rstr2b64(rstr_hmac(k, d), b64pad);
37555               };
37556               this.any_hmac = function(k, d, e) {
37557                 return rstr2any(rstr_hmac(k, d), e);
37558               };
37559               /**
37560                * Perform a simple self-test to see if the VM is working
37561                * @return {String} Hexadecimal hash sample
37562                * @public
37563                */
37564               this.vm_test = function() {
37565                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
37566               };
37567               /**
37568                * @description Enable/disable uppercase hexadecimal returned string
37569                * @param {boolean}
37570                * @return {Object} this
37571                * @public
37572                */
37573               this.setUpperCase = function(a) {
37574                 if (typeof a === 'boolean') {
37575                   hexcase = a;
37576                 }
37577                 return this;
37578               };
37579               /**
37580                * @description Defines a base64 pad string
37581                * @param {string} Pad
37582                * @return {Object} this
37583                * @public
37584                */
37585               this.setPad = function(a) {
37586                 b64pad = a || b64pad;
37587                 return this;
37588               };
37589               /**
37590                * @description Defines a base64 pad string
37591                * @param {boolean}
37592                * @return {Object} this
37593                * @public
37594                */
37595               this.setUTF8 = function(a) {
37596                 if (typeof a === 'boolean') {
37597                   utf8 = a;
37598                 }
37599                 return this;
37600               };
37601
37602               /* private methods */
37603
37604               /**
37605                * Calculate the SHA-512 of a raw string
37606                */
37607
37608               function rstr(s) {
37609                 s = (utf8) ? utf8Encode(s) : s;
37610                 return binb2rstr(binb(rstr2binb(s), s.length * 8));
37611               }
37612               /*
37613                * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
37614                */
37615
37616               function rstr_hmac(key, data) {
37617                 key = (utf8) ? utf8Encode(key) : key;
37618                 data = (utf8) ? utf8Encode(data) : data;
37619
37620                 var hash, i = 0,
37621                   bkey = rstr2binb(key),
37622                   ipad = Array(32),
37623                   opad = Array(32);
37624
37625                 if (bkey.length > 32) {
37626                   bkey = binb(bkey, key.length * 8);
37627                 }
37628
37629                 for (; i < 32; i += 1) {
37630                   ipad[i] = bkey[i] ^ 0x36363636;
37631                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
37632                 }
37633
37634                 hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
37635                 return binb2rstr(binb(opad.concat(hash), 1024 + 512));
37636               }
37637
37638               /**
37639                * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
37640                */
37641
37642               function binb(x, len) {
37643                 var j, i, l,
37644                   W = new Array(80),
37645                   hash = new Array(16),
37646                   //Initial hash values
37647                   H = [
37648                     new int64(0x6a09e667, -205731576),
37649                     new int64(-1150833019, -2067093701),
37650                     new int64(0x3c6ef372, -23791573),
37651                     new int64(-1521486534, 0x5f1d36f1),
37652                     new int64(0x510e527f, -1377402159),
37653                     new int64(-1694144372, 0x2b3e6c1f),
37654                     new int64(0x1f83d9ab, -79577749),
37655                     new int64(0x5be0cd19, 0x137e2179)
37656                   ],
37657                   T1 = new int64(0, 0),
37658                   T2 = new int64(0, 0),
37659                   a = new int64(0, 0),
37660                   b = new int64(0, 0),
37661                   c = new int64(0, 0),
37662                   d = new int64(0, 0),
37663                   e = new int64(0, 0),
37664                   f = new int64(0, 0),
37665                   g = new int64(0, 0),
37666                   h = new int64(0, 0),
37667                   //Temporary variables not specified by the document
37668                   s0 = new int64(0, 0),
37669                   s1 = new int64(0, 0),
37670                   Ch = new int64(0, 0),
37671                   Maj = new int64(0, 0),
37672                   r1 = new int64(0, 0),
37673                   r2 = new int64(0, 0),
37674                   r3 = new int64(0, 0);
37675
37676                 if (sha512_k === undefined) {
37677                   //SHA512 constants
37678                   sha512_k = [
37679                     new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd),
37680                     new int64(-1245643825, -330482897), new int64(-373957723, -2121671748),
37681                     new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031),
37682                     new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736),
37683                     new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe),
37684                     new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302),
37685                     new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1),
37686                     new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428),
37687                     new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3),
37688                     new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65),
37689                     new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
37690                     new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459),
37691                     new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210),
37692                     new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340),
37693                     new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395),
37694                     new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70),
37695                     new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
37696                     new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473),
37697                     new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8),
37698                     new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b),
37699                     new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023),
37700                     new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30),
37701                     new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910),
37702                     new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
37703                     new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53),
37704                     new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016),
37705                     new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893),
37706                     new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397),
37707                     new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
37708                     new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec),
37709                     new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047),
37710                     new int64(-1090935817, -1295615723), new int64(-965641998, -479046869),
37711                     new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207),
37712                     new int64(-354779690, -840897762), new int64(-176337025, -294727304),
37713                     new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026),
37714                     new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b),
37715                     new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
37716                     new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620),
37717                     new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430),
37718                     new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)
37719                   ];
37720                 }
37721
37722                 for (i = 0; i < 80; i += 1) {
37723                   W[i] = new int64(0, 0);
37724                 }
37725
37726                 // append padding to the source string. The format is described in the FIPS.
37727                 x[len >> 5] |= 0x80 << (24 - (len & 0x1f));
37728                 x[((len + 128 >> 10) << 5) + 31] = len;
37729                 l = x.length;
37730                 for (i = 0; i < l; i += 32) { //32 dwords is the block size
37731                   int64copy(a, H[0]);
37732                   int64copy(b, H[1]);
37733                   int64copy(c, H[2]);
37734                   int64copy(d, H[3]);
37735                   int64copy(e, H[4]);
37736                   int64copy(f, H[5]);
37737                   int64copy(g, H[6]);
37738                   int64copy(h, H[7]);
37739
37740                   for (j = 0; j < 16; j += 1) {
37741                     W[j].h = x[i + 2 * j];
37742                     W[j].l = x[i + 2 * j + 1];
37743                   }
37744
37745                   for (j = 16; j < 80; j += 1) {
37746                     //sigma1
37747                     int64rrot(r1, W[j - 2], 19);
37748                     int64revrrot(r2, W[j - 2], 29);
37749                     int64shr(r3, W[j - 2], 6);
37750                     s1.l = r1.l ^ r2.l ^ r3.l;
37751                     s1.h = r1.h ^ r2.h ^ r3.h;
37752                     //sigma0
37753                     int64rrot(r1, W[j - 15], 1);
37754                     int64rrot(r2, W[j - 15], 8);
37755                     int64shr(r3, W[j - 15], 7);
37756                     s0.l = r1.l ^ r2.l ^ r3.l;
37757                     s0.h = r1.h ^ r2.h ^ r3.h;
37758
37759                     int64add4(W[j], s1, W[j - 7], s0, W[j - 16]);
37760                   }
37761
37762                   for (j = 0; j < 80; j += 1) {
37763                     //Ch
37764                     Ch.l = (e.l & f.l) ^ (~e.l & g.l);
37765                     Ch.h = (e.h & f.h) ^ (~e.h & g.h);
37766
37767                     //Sigma1
37768                     int64rrot(r1, e, 14);
37769                     int64rrot(r2, e, 18);
37770                     int64revrrot(r3, e, 9);
37771                     s1.l = r1.l ^ r2.l ^ r3.l;
37772                     s1.h = r1.h ^ r2.h ^ r3.h;
37773
37774                     //Sigma0
37775                     int64rrot(r1, a, 28);
37776                     int64revrrot(r2, a, 2);
37777                     int64revrrot(r3, a, 7);
37778                     s0.l = r1.l ^ r2.l ^ r3.l;
37779                     s0.h = r1.h ^ r2.h ^ r3.h;
37780
37781                     //Maj
37782                     Maj.l = (a.l & b.l) ^ (a.l & c.l) ^ (b.l & c.l);
37783                     Maj.h = (a.h & b.h) ^ (a.h & c.h) ^ (b.h & c.h);
37784
37785                     int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
37786                     int64add(T2, s0, Maj);
37787
37788                     int64copy(h, g);
37789                     int64copy(g, f);
37790                     int64copy(f, e);
37791                     int64add(e, d, T1);
37792                     int64copy(d, c);
37793                     int64copy(c, b);
37794                     int64copy(b, a);
37795                     int64add(a, T1, T2);
37796                   }
37797                   int64add(H[0], H[0], a);
37798                   int64add(H[1], H[1], b);
37799                   int64add(H[2], H[2], c);
37800                   int64add(H[3], H[3], d);
37801                   int64add(H[4], H[4], e);
37802                   int64add(H[5], H[5], f);
37803                   int64add(H[6], H[6], g);
37804                   int64add(H[7], H[7], h);
37805                 }
37806
37807                 //represent the hash as an array of 32-bit dwords
37808                 for (i = 0; i < 8; i += 1) {
37809                   hash[2 * i] = H[i].h;
37810                   hash[2 * i + 1] = H[i].l;
37811                 }
37812                 return hash;
37813               }
37814
37815               //A constructor for 64-bit numbers
37816
37817               function int64(h, l) {
37818                 this.h = h;
37819                 this.l = l;
37820                 //this.toString = int64toString;
37821               }
37822
37823               //Copies src into dst, assuming both are 64-bit numbers
37824
37825               function int64copy(dst, src) {
37826                 dst.h = src.h;
37827                 dst.l = src.l;
37828               }
37829
37830               //Right-rotates a 64-bit number by shift
37831               //Won't handle cases of shift>=32
37832               //The function revrrot() is for that
37833
37834               function int64rrot(dst, x, shift) {
37835                 dst.l = (x.l >>> shift) | (x.h << (32 - shift));
37836                 dst.h = (x.h >>> shift) | (x.l << (32 - shift));
37837               }
37838
37839               //Reverses the dwords of the source and then rotates right by shift.
37840               //This is equivalent to rotation by 32+shift
37841
37842               function int64revrrot(dst, x, shift) {
37843                 dst.l = (x.h >>> shift) | (x.l << (32 - shift));
37844                 dst.h = (x.l >>> shift) | (x.h << (32 - shift));
37845               }
37846
37847               //Bitwise-shifts right a 64-bit number by shift
37848               //Won't handle shift>=32, but it's never needed in SHA512
37849
37850               function int64shr(dst, x, shift) {
37851                 dst.l = (x.l >>> shift) | (x.h << (32 - shift));
37852                 dst.h = (x.h >>> shift);
37853               }
37854
37855               //Adds two 64-bit numbers
37856               //Like the original implementation, does not rely on 32-bit operations
37857
37858               function int64add(dst, x, y) {
37859                 var w0 = (x.l & 0xffff) + (y.l & 0xffff);
37860                 var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
37861                 var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
37862                 var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
37863                 dst.l = (w0 & 0xffff) | (w1 << 16);
37864                 dst.h = (w2 & 0xffff) | (w3 << 16);
37865               }
37866
37867               //Same, except with 4 addends. Works faster than adding them one by one.
37868
37869               function int64add4(dst, a, b, c, d) {
37870                 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
37871                 var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
37872                 var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
37873                 var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
37874                 dst.l = (w0 & 0xffff) | (w1 << 16);
37875                 dst.h = (w2 & 0xffff) | (w3 << 16);
37876               }
37877
37878               //Same, except with 5 addends
37879
37880               function int64add5(dst, a, b, c, d, e) {
37881                 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
37882                   w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
37883                   w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
37884                   w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
37885                 dst.l = (w0 & 0xffff) | (w1 << 16);
37886                 dst.h = (w2 & 0xffff) | (w3 << 16);
37887               }
37888             },
37889             /**
37890              * @class Hashes.RMD160
37891              * @constructor
37892              * @param {Object} [config]
37893              *
37894              * A JavaScript implementation of the RIPEMD-160 Algorithm
37895              * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
37896              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
37897              * See http://pajhome.org.uk/crypt/md5 for details.
37898              * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
37899              */
37900             RMD160: function(options) {
37901               /**
37902                * Private properties configuration variables. You may need to tweak these to be compatible with
37903                * the server-side, but the defaults work in most cases.
37904                * @see this.setUpperCase() method
37905                * @see this.setPad() method
37906                */
37907               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false,
37908                 /* hexadecimal output case format. false - lowercase; true - uppercase  */
37909                 b64pad = (options && typeof options.pad === 'string') ? options.pa : '=',
37910                 /* base-64 pad character. Default '=' for strict RFC compliance   */
37911                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true,
37912                 /* enable/disable utf8 encoding */
37913                 rmd160_r1 = [
37914                   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
37915                   7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
37916                   3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
37917                   1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
37918                   4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13
37919                 ],
37920                 rmd160_r2 = [
37921                   5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
37922                   6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
37923                   15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
37924                   8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
37925                   12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11
37926                 ],
37927                 rmd160_s1 = [
37928                   11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
37929                   7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
37930                   11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
37931                   11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
37932                   9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6
37933                 ],
37934                 rmd160_s2 = [
37935                   8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
37936                   9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
37937                   9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
37938                   15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
37939                   8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11
37940                 ];
37941
37942               /* privileged (public) methods */
37943               this.hex = function(s) {
37944                 return rstr2hex(rstr(s));
37945               };
37946               this.b64 = function(s) {
37947                 return rstr2b64(rstr(s), b64pad);
37948               };
37949               this.any = function(s, e) {
37950                 return rstr2any(rstr(s), e);
37951               };
37952               this.raw = function(s) {
37953                 return rstr(s);
37954               };
37955               this.hex_hmac = function(k, d) {
37956                 return rstr2hex(rstr_hmac(k, d));
37957               };
37958               this.b64_hmac = function(k, d) {
37959                 return rstr2b64(rstr_hmac(k, d), b64pad);
37960               };
37961               this.any_hmac = function(k, d, e) {
37962                 return rstr2any(rstr_hmac(k, d), e);
37963               };
37964               /**
37965                * Perform a simple self-test to see if the VM is working
37966                * @return {String} Hexadecimal hash sample
37967                * @public
37968                */
37969               this.vm_test = function() {
37970                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
37971               };
37972               /**
37973                * @description Enable/disable uppercase hexadecimal returned string
37974                * @param {boolean}
37975                * @return {Object} this
37976                * @public
37977                */
37978               this.setUpperCase = function(a) {
37979                 if (typeof a === 'boolean') {
37980                   hexcase = a;
37981                 }
37982                 return this;
37983               };
37984               /**
37985                * @description Defines a base64 pad string
37986                * @param {string} Pad
37987                * @return {Object} this
37988                * @public
37989                */
37990               this.setPad = function(a) {
37991                 if (typeof a !== 'undefined') {
37992                   b64pad = a;
37993                 }
37994                 return this;
37995               };
37996               /**
37997                * @description Defines a base64 pad string
37998                * @param {boolean}
37999                * @return {Object} this
38000                * @public
38001                */
38002               this.setUTF8 = function(a) {
38003                 if (typeof a === 'boolean') {
38004                   utf8 = a;
38005                 }
38006                 return this;
38007               };
38008
38009               /* private methods */
38010
38011               /**
38012                * Calculate the rmd160 of a raw string
38013                */
38014
38015               function rstr(s) {
38016                 s = (utf8) ? utf8Encode(s) : s;
38017                 return binl2rstr(binl(rstr2binl(s), s.length * 8));
38018               }
38019
38020               /**
38021                * Calculate the HMAC-rmd160 of a key and some data (raw strings)
38022                */
38023
38024               function rstr_hmac(key, data) {
38025                 key = (utf8) ? utf8Encode(key) : key;
38026                 data = (utf8) ? utf8Encode(data) : data;
38027                 var i, hash,
38028                   bkey = rstr2binl(key),
38029                   ipad = Array(16),
38030                   opad = Array(16);
38031
38032                 if (bkey.length > 16) {
38033                   bkey = binl(bkey, key.length * 8);
38034                 }
38035
38036                 for (i = 0; i < 16; i += 1) {
38037                   ipad[i] = bkey[i] ^ 0x36363636;
38038                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
38039                 }
38040                 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
38041                 return binl2rstr(binl(opad.concat(hash), 512 + 160));
38042               }
38043
38044               /**
38045                * Convert an array of little-endian words to a string
38046                */
38047
38048               function binl2rstr(input) {
38049                 var i, output = '',
38050                   l = input.length * 32;
38051                 for (i = 0; i < l; i += 8) {
38052                   output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
38053                 }
38054                 return output;
38055               }
38056
38057               /**
38058                * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
38059                */
38060
38061               function binl(x, len) {
38062                 var T, j, i, l,
38063                   h0 = 0x67452301,
38064                   h1 = 0xefcdab89,
38065                   h2 = 0x98badcfe,
38066                   h3 = 0x10325476,
38067                   h4 = 0xc3d2e1f0,
38068                   A1, B1, C1, D1, E1,
38069                   A2, B2, C2, D2, E2;
38070
38071                 /* append padding */
38072                 x[len >> 5] |= 0x80 << (len % 32);
38073                 x[(((len + 64) >>> 9) << 4) + 14] = len;
38074                 l = x.length;
38075
38076                 for (i = 0; i < l; i += 16) {
38077                   A1 = A2 = h0;
38078                   B1 = B2 = h1;
38079                   C1 = C2 = h2;
38080                   D1 = D2 = h3;
38081                   E1 = E2 = h4;
38082                   for (j = 0; j <= 79; j += 1) {
38083                     T = safe_add(A1, rmd160_f(j, B1, C1, D1));
38084                     T = safe_add(T, x[i + rmd160_r1[j]]);
38085                     T = safe_add(T, rmd160_K1(j));
38086                     T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
38087                     A1 = E1;
38088                     E1 = D1;
38089                     D1 = bit_rol(C1, 10);
38090                     C1 = B1;
38091                     B1 = T;
38092                     T = safe_add(A2, rmd160_f(79 - j, B2, C2, D2));
38093                     T = safe_add(T, x[i + rmd160_r2[j]]);
38094                     T = safe_add(T, rmd160_K2(j));
38095                     T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
38096                     A2 = E2;
38097                     E2 = D2;
38098                     D2 = bit_rol(C2, 10);
38099                     C2 = B2;
38100                     B2 = T;
38101                   }
38102
38103                   T = safe_add(h1, safe_add(C1, D2));
38104                   h1 = safe_add(h2, safe_add(D1, E2));
38105                   h2 = safe_add(h3, safe_add(E1, A2));
38106                   h3 = safe_add(h4, safe_add(A1, B2));
38107                   h4 = safe_add(h0, safe_add(B1, C2));
38108                   h0 = T;
38109                 }
38110                 return [h0, h1, h2, h3, h4];
38111               }
38112
38113               // specific algorithm methods
38114
38115               function rmd160_f(j, x, y, z) {
38116                 return (0 <= j && j <= 15) ? (x ^ y ^ z) :
38117                   (16 <= j && j <= 31) ? (x & y) | (~x & z) :
38118                   (32 <= j && j <= 47) ? (x | ~y) ^ z :
38119                   (48 <= j && j <= 63) ? (x & z) | (y & ~z) :
38120                   (64 <= j && j <= 79) ? x ^ (y | ~z) :
38121                   'rmd160_f: j out of range';
38122               }
38123
38124               function rmd160_K1(j) {
38125                 return (0 <= j && j <= 15) ? 0x00000000 :
38126                   (16 <= j && j <= 31) ? 0x5a827999 :
38127                   (32 <= j && j <= 47) ? 0x6ed9eba1 :
38128                   (48 <= j && j <= 63) ? 0x8f1bbcdc :
38129                   (64 <= j && j <= 79) ? 0xa953fd4e :
38130                   'rmd160_K1: j out of range';
38131               }
38132
38133               function rmd160_K2(j) {
38134                 return (0 <= j && j <= 15) ? 0x50a28be6 :
38135                   (16 <= j && j <= 31) ? 0x5c4dd124 :
38136                   (32 <= j && j <= 47) ? 0x6d703ef3 :
38137                   (48 <= j && j <= 63) ? 0x7a6d76e9 :
38138                   (64 <= j && j <= 79) ? 0x00000000 :
38139                   'rmd160_K2: j out of range';
38140               }
38141             }
38142           };
38143
38144           // exposes Hashes
38145           (function(window, undefined$1) {
38146             var freeExports = false;
38147             {
38148               freeExports = exports;
38149               if (exports && typeof commonjsGlobal === 'object' && commonjsGlobal && commonjsGlobal === commonjsGlobal.global) {
38150                 window = commonjsGlobal;
38151               }
38152             }
38153
38154             if (typeof undefined$1 === 'function' && typeof undefined$1.amd === 'object' && undefined$1.amd) {
38155               // define as an anonymous module, so, through path mapping, it can be aliased
38156               undefined$1(function() {
38157                 return Hashes;
38158               });
38159             } else if (freeExports) {
38160               // in Node.js or RingoJS v0.8.0+
38161               if ( module && module.exports === freeExports) {
38162                 module.exports = Hashes;
38163               }
38164               // in Narwhal or RingoJS v0.7.0-
38165               else {
38166                 freeExports.Hashes = Hashes;
38167               }
38168             } else {
38169               // in a browser or Rhino
38170               window.Hashes = Hashes;
38171             }
38172           }(this));
38173         }()); // IIFE
38174         });
38175
38176         var immutable = extend$2;
38177
38178         var hasOwnProperty$3 = Object.prototype.hasOwnProperty;
38179
38180         function extend$2() {
38181             var target = {};
38182
38183             for (var i = 0; i < arguments.length; i++) {
38184                 var source = arguments[i];
38185
38186                 for (var key in source) {
38187                     if (hasOwnProperty$3.call(source, key)) {
38188                         target[key] = source[key];
38189                     }
38190                 }
38191             }
38192
38193             return target
38194         }
38195
38196         var sha1 = new hashes.SHA1();
38197
38198         var ohauth = {};
38199
38200         ohauth.qsString = function(obj) {
38201             return Object.keys(obj).sort().map(function(key) {
38202                 return ohauth.percentEncode(key) + '=' +
38203                     ohauth.percentEncode(obj[key]);
38204             }).join('&');
38205         };
38206
38207         ohauth.stringQs = function(str) {
38208             return str.split('&').filter(function (pair) {
38209                 return pair !== '';
38210             }).reduce(function(obj, pair){
38211                 var parts = pair.split('=');
38212                 obj[decodeURIComponent(parts[0])] = (null === parts[1]) ?
38213                     '' : decodeURIComponent(parts[1]);
38214                 return obj;
38215             }, {});
38216         };
38217
38218         ohauth.rawxhr = function(method, url, data, headers, callback) {
38219             var xhr = new XMLHttpRequest(),
38220                 twoHundred = /^20\d$/;
38221             xhr.onreadystatechange = function() {
38222                 if (4 === xhr.readyState && 0 !== xhr.status) {
38223                     if (twoHundred.test(xhr.status)) callback(null, xhr);
38224                     else return callback(xhr, null);
38225                 }
38226             };
38227             xhr.onerror = function(e) { return callback(e, null); };
38228             xhr.open(method, url, true);
38229             for (var h in headers) xhr.setRequestHeader(h, headers[h]);
38230             xhr.send(data);
38231             return xhr;
38232         };
38233
38234         ohauth.xhr = function(method, url, auth, data, options, callback) {
38235             var headers = (options && options.header) || {
38236                 'Content-Type': 'application/x-www-form-urlencoded'
38237             };
38238             headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
38239             return ohauth.rawxhr(method, url, data, headers, callback);
38240         };
38241
38242         ohauth.nonce = function() {
38243             for (var o = ''; o.length < 6;) {
38244                 o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
38245             }
38246             return o;
38247         };
38248
38249         ohauth.authHeader = function(obj) {
38250             return Object.keys(obj).sort().map(function(key) {
38251                 return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
38252             }).join(', ');
38253         };
38254
38255         ohauth.timestamp = function() { return ~~((+new Date()) / 1000); };
38256
38257         ohauth.percentEncode = function(s) {
38258             return encodeURIComponent(s)
38259                 .replace(/\!/g, '%21').replace(/\'/g, '%27')
38260                 .replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
38261         };
38262
38263         ohauth.baseString = function(method, url, params) {
38264             if (params.oauth_signature) delete params.oauth_signature;
38265             return [
38266                 method,
38267                 ohauth.percentEncode(url),
38268                 ohauth.percentEncode(ohauth.qsString(params))].join('&');
38269         };
38270
38271         ohauth.signature = function(oauth_secret, token_secret, baseString) {
38272             return sha1.b64_hmac(
38273                 ohauth.percentEncode(oauth_secret) + '&' +
38274                 ohauth.percentEncode(token_secret),
38275                 baseString);
38276         };
38277
38278         /**
38279          * Takes an options object for configuration (consumer_key,
38280          * consumer_secret, version, signature_method, token, token_secret)
38281          * and returns a function that generates the Authorization header
38282          * for given data.
38283          *
38284          * The returned function takes these parameters:
38285          * - method: GET/POST/...
38286          * - uri: full URI with protocol, port, path and query string
38287          * - extra_params: any extra parameters (that are passed in the POST data),
38288          *   can be an object or a from-urlencoded string.
38289          *
38290          * Returned function returns full OAuth header with "OAuth" string in it.
38291          */
38292
38293         ohauth.headerGenerator = function(options) {
38294             options = options || {};
38295             var consumer_key = options.consumer_key || '',
38296                 consumer_secret = options.consumer_secret || '',
38297                 signature_method = options.signature_method || 'HMAC-SHA1',
38298                 version = options.version || '1.0',
38299                 token = options.token || '',
38300                 token_secret = options.token_secret || '';
38301
38302             return function(method, uri, extra_params) {
38303                 method = method.toUpperCase();
38304                 if (typeof extra_params === 'string' && extra_params.length > 0) {
38305                     extra_params = ohauth.stringQs(extra_params);
38306                 }
38307
38308                 var uri_parts = uri.split('?', 2),
38309                 base_uri = uri_parts[0];
38310
38311                 var query_params = uri_parts.length === 2 ?
38312                     ohauth.stringQs(uri_parts[1]) : {};
38313
38314                 var oauth_params = {
38315                     oauth_consumer_key: consumer_key,
38316                     oauth_signature_method: signature_method,
38317                     oauth_version: version,
38318                     oauth_timestamp: ohauth.timestamp(),
38319                     oauth_nonce: ohauth.nonce()
38320                 };
38321
38322                 if (token) oauth_params.oauth_token = token;
38323
38324                 var all_params = immutable({}, oauth_params, query_params, extra_params),
38325                     base_str = ohauth.baseString(method, base_uri, all_params);
38326
38327                 oauth_params.oauth_signature = ohauth.signature(consumer_secret, token_secret, base_str);
38328
38329                 return 'OAuth ' + ohauth.authHeader(oauth_params);
38330             };
38331         };
38332
38333         var ohauth_1 = ohauth;
38334
38335         var resolveUrl$1 = createCommonjsModule(function (module, exports) {
38336         // Copyright 2014 Simon Lydell
38337         // X11 (“MIT”) Licensed. (See LICENSE.)
38338
38339         void (function(root, factory) {
38340           {
38341             module.exports = factory();
38342           }
38343         }(commonjsGlobal, function() {
38344
38345           function resolveUrl(/* ...urls */) {
38346             var numUrls = arguments.length;
38347
38348             if (numUrls === 0) {
38349               throw new Error("resolveUrl requires at least one argument; got none.")
38350             }
38351
38352             var base = document.createElement("base");
38353             base.href = arguments[0];
38354
38355             if (numUrls === 1) {
38356               return base.href
38357             }
38358
38359             var head = document.getElementsByTagName("head")[0];
38360             head.insertBefore(base, head.firstChild);
38361
38362             var a = document.createElement("a");
38363             var resolved;
38364
38365             for (var index = 1; index < numUrls; index++) {
38366               a.href = arguments[index];
38367               resolved = a.href;
38368               base.href = resolved;
38369             }
38370
38371             head.removeChild(base);
38372
38373             return resolved
38374           }
38375
38376           return resolveUrl
38377
38378         }));
38379         });
38380
38381         var assign$1 = make_assign();
38382         var create$8 = make_create();
38383         var trim = make_trim();
38384         var Global = (typeof window !== 'undefined' ? window : commonjsGlobal);
38385
38386         var util = {
38387                 assign: assign$1,
38388                 create: create$8,
38389                 trim: trim,
38390                 bind: bind$3,
38391                 slice: slice$5,
38392                 each: each,
38393                 map: map$5,
38394                 pluck: pluck,
38395                 isList: isList,
38396                 isFunction: isFunction$2,
38397                 isObject: isObject$2,
38398                 Global: Global
38399         };
38400
38401         function make_assign() {
38402                 if (Object.assign) {
38403                         return Object.assign
38404                 } else {
38405                         return function shimAssign(obj, props1, props2, etc) {
38406                                 for (var i = 1; i < arguments.length; i++) {
38407                                         each(Object(arguments[i]), function(val, key) {
38408                                                 obj[key] = val;
38409                                         });
38410                                 }                       
38411                                 return obj
38412                         }
38413                 }
38414         }
38415
38416         function make_create() {
38417                 if (Object.create) {
38418                         return function create(obj, assignProps1, assignProps2, etc) {
38419                                 var assignArgsList = slice$5(arguments, 1);
38420                                 return assign$1.apply(this, [Object.create(obj)].concat(assignArgsList))
38421                         }
38422                 } else {
38423                         function F() {} // eslint-disable-line no-inner-declarations
38424                         return function create(obj, assignProps1, assignProps2, etc) {
38425                                 var assignArgsList = slice$5(arguments, 1);
38426                                 F.prototype = obj;
38427                                 return assign$1.apply(this, [new F()].concat(assignArgsList))
38428                         }
38429                 }
38430         }
38431
38432         function make_trim() {
38433                 if (String.prototype.trim) {
38434                         return function trim(str) {
38435                                 return String.prototype.trim.call(str)
38436                         }
38437                 } else {
38438                         return function trim(str) {
38439                                 return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')
38440                         }
38441                 }
38442         }
38443
38444         function bind$3(obj, fn) {
38445                 return function() {
38446                         return fn.apply(obj, Array.prototype.slice.call(arguments, 0))
38447                 }
38448         }
38449
38450         function slice$5(arr, index) {
38451                 return Array.prototype.slice.call(arr, index || 0)
38452         }
38453
38454         function each(obj, fn) {
38455                 pluck(obj, function(val, key) {
38456                         fn(val, key);
38457                         return false
38458                 });
38459         }
38460
38461         function map$5(obj, fn) {
38462                 var res = (isList(obj) ? [] : {});
38463                 pluck(obj, function(v, k) {
38464                         res[k] = fn(v, k);
38465                         return false
38466                 });
38467                 return res
38468         }
38469
38470         function pluck(obj, fn) {
38471                 if (isList(obj)) {
38472                         for (var i=0; i<obj.length; i++) {
38473                                 if (fn(obj[i], i)) {
38474                                         return obj[i]
38475                                 }
38476                         }
38477                 } else {
38478                         for (var key in obj) {
38479                                 if (obj.hasOwnProperty(key)) {
38480                                         if (fn(obj[key], key)) {
38481                                                 return obj[key]
38482                                         }
38483                                 }
38484                         }
38485                 }
38486         }
38487
38488         function isList(val) {
38489                 return (val != null && typeof val != 'function' && typeof val.length == 'number')
38490         }
38491
38492         function isFunction$2(val) {
38493                 return val && {}.toString.call(val) === '[object Function]'
38494         }
38495
38496         function isObject$2(val) {
38497                 return val && {}.toString.call(val) === '[object Object]'
38498         }
38499
38500         var slice$6 = util.slice;
38501         var pluck$1 = util.pluck;
38502         var each$1 = util.each;
38503         var bind$4 = util.bind;
38504         var create$9 = util.create;
38505         var isList$1 = util.isList;
38506         var isFunction$3 = util.isFunction;
38507         var isObject$3 = util.isObject;
38508
38509         var storeEngine = {
38510                 createStore: createStore
38511         };
38512
38513         var storeAPI = {
38514                 version: '2.0.12',
38515                 enabled: false,
38516                 
38517                 // get returns the value of the given key. If that value
38518                 // is undefined, it returns optionalDefaultValue instead.
38519                 get: function(key, optionalDefaultValue) {
38520                         var data = this.storage.read(this._namespacePrefix + key);
38521                         return this._deserialize(data, optionalDefaultValue)
38522                 },
38523
38524                 // set will store the given value at key and returns value.
38525                 // Calling set with value === undefined is equivalent to calling remove.
38526                 set: function(key, value) {
38527                         if (value === undefined) {
38528                                 return this.remove(key)
38529                         }
38530                         this.storage.write(this._namespacePrefix + key, this._serialize(value));
38531                         return value
38532                 },
38533
38534                 // remove deletes the key and value stored at the given key.
38535                 remove: function(key) {
38536                         this.storage.remove(this._namespacePrefix + key);
38537                 },
38538
38539                 // each will call the given callback once for each key-value pair
38540                 // in this store.
38541                 each: function(callback) {
38542                         var self = this;
38543                         this.storage.each(function(val, namespacedKey) {
38544                                 callback.call(self, self._deserialize(val), (namespacedKey || '').replace(self._namespaceRegexp, ''));
38545                         });
38546                 },
38547
38548                 // clearAll will remove all the stored key-value pairs in this store.
38549                 clearAll: function() {
38550                         this.storage.clearAll();
38551                 },
38552
38553                 // additional functionality that can't live in plugins
38554                 // ---------------------------------------------------
38555
38556                 // hasNamespace returns true if this store instance has the given namespace.
38557                 hasNamespace: function(namespace) {
38558                         return (this._namespacePrefix == '__storejs_'+namespace+'_')
38559                 },
38560
38561                 // createStore creates a store.js instance with the first
38562                 // functioning storage in the list of storage candidates,
38563                 // and applies the the given mixins to the instance.
38564                 createStore: function() {
38565                         return createStore.apply(this, arguments)
38566                 },
38567                 
38568                 addPlugin: function(plugin) {
38569                         this._addPlugin(plugin);
38570                 },
38571                 
38572                 namespace: function(namespace) {
38573                         return createStore(this.storage, this.plugins, namespace)
38574                 }
38575         };
38576
38577         function _warn() {
38578                 var _console = (typeof console == 'undefined' ? null : console);
38579                 if (!_console) { return }
38580                 var fn = (_console.warn ? _console.warn : _console.log);
38581                 fn.apply(_console, arguments);
38582         }
38583
38584         function createStore(storages, plugins, namespace) {
38585                 if (!namespace) {
38586                         namespace = '';
38587                 }
38588                 if (storages && !isList$1(storages)) {
38589                         storages = [storages];
38590                 }
38591                 if (plugins && !isList$1(plugins)) {
38592                         plugins = [plugins];
38593                 }
38594
38595                 var namespacePrefix = (namespace ? '__storejs_'+namespace+'_' : '');
38596                 var namespaceRegexp = (namespace ? new RegExp('^'+namespacePrefix) : null);
38597                 var legalNamespaces = /^[a-zA-Z0-9_\-]*$/; // alpha-numeric + underscore and dash
38598                 if (!legalNamespaces.test(namespace)) {
38599                         throw new Error('store.js namespaces can only have alphanumerics + underscores and dashes')
38600                 }
38601                 
38602                 var _privateStoreProps = {
38603                         _namespacePrefix: namespacePrefix,
38604                         _namespaceRegexp: namespaceRegexp,
38605
38606                         _testStorage: function(storage) {
38607                                 try {
38608                                         var testStr = '__storejs__test__';
38609                                         storage.write(testStr, testStr);
38610                                         var ok = (storage.read(testStr) === testStr);
38611                                         storage.remove(testStr);
38612                                         return ok
38613                                 } catch(e) {
38614                                         return false
38615                                 }
38616                         },
38617
38618                         _assignPluginFnProp: function(pluginFnProp, propName) {
38619                                 var oldFn = this[propName];
38620                                 this[propName] = function pluginFn() {
38621                                         var args = slice$6(arguments, 0);
38622                                         var self = this;
38623
38624                                         // super_fn calls the old function which was overwritten by
38625                                         // this mixin.
38626                                         function super_fn() {
38627                                                 if (!oldFn) { return }
38628                                                 each$1(arguments, function(arg, i) {
38629                                                         args[i] = arg;
38630                                                 });
38631                                                 return oldFn.apply(self, args)
38632                                         }
38633
38634                                         // Give mixing function access to super_fn by prefixing all mixin function
38635                                         // arguments with super_fn.
38636                                         var newFnArgs = [super_fn].concat(args);
38637
38638                                         return pluginFnProp.apply(self, newFnArgs)
38639                                 };
38640                         },
38641
38642                         _serialize: function(obj) {
38643                                 return JSON.stringify(obj)
38644                         },
38645
38646                         _deserialize: function(strVal, defaultVal) {
38647                                 if (!strVal) { return defaultVal }
38648                                 // It is possible that a raw string value has been previously stored
38649                                 // in a storage without using store.js, meaning it will be a raw
38650                                 // string value instead of a JSON serialized string. By defaulting
38651                                 // to the raw string value in case of a JSON parse error, we allow
38652                                 // for past stored values to be forwards-compatible with store.js
38653                                 var val = '';
38654                                 try { val = JSON.parse(strVal); }
38655                                 catch(e) { val = strVal; }
38656
38657                                 return (val !== undefined ? val : defaultVal)
38658                         },
38659                         
38660                         _addStorage: function(storage) {
38661                                 if (this.enabled) { return }
38662                                 if (this._testStorage(storage)) {
38663                                         this.storage = storage;
38664                                         this.enabled = true;
38665                                 }
38666                         },
38667
38668                         _addPlugin: function(plugin) {
38669                                 var self = this;
38670
38671                                 // If the plugin is an array, then add all plugins in the array.
38672                                 // This allows for a plugin to depend on other plugins.
38673                                 if (isList$1(plugin)) {
38674                                         each$1(plugin, function(plugin) {
38675                                                 self._addPlugin(plugin);
38676                                         });
38677                                         return
38678                                 }
38679
38680                                 // Keep track of all plugins we've seen so far, so that we
38681                                 // don't add any of them twice.
38682                                 var seenPlugin = pluck$1(this.plugins, function(seenPlugin) {
38683                                         return (plugin === seenPlugin)
38684                                 });
38685                                 if (seenPlugin) {
38686                                         return
38687                                 }
38688                                 this.plugins.push(plugin);
38689
38690                                 // Check that the plugin is properly formed
38691                                 if (!isFunction$3(plugin)) {
38692                                         throw new Error('Plugins must be function values that return objects')
38693                                 }
38694
38695                                 var pluginProperties = plugin.call(this);
38696                                 if (!isObject$3(pluginProperties)) {
38697                                         throw new Error('Plugins must return an object of function properties')
38698                                 }
38699
38700                                 // Add the plugin function properties to this store instance.
38701                                 each$1(pluginProperties, function(pluginFnProp, propName) {
38702                                         if (!isFunction$3(pluginFnProp)) {
38703                                                 throw new Error('Bad plugin property: '+propName+' from plugin '+plugin.name+'. Plugins should only return functions.')
38704                                         }
38705                                         self._assignPluginFnProp(pluginFnProp, propName);
38706                                 });
38707                         },
38708                         
38709                         // Put deprecated properties in the private API, so as to not expose it to accidential
38710                         // discovery through inspection of the store object.
38711                         
38712                         // Deprecated: addStorage
38713                         addStorage: function(storage) {
38714                                 _warn('store.addStorage(storage) is deprecated. Use createStore([storages])');
38715                                 this._addStorage(storage);
38716                         }
38717                 };
38718
38719                 var store = create$9(_privateStoreProps, storeAPI, {
38720                         plugins: []
38721                 });
38722                 store.raw = {};
38723                 each$1(store, function(prop, propName) {
38724                         if (isFunction$3(prop)) {
38725                                 store.raw[propName] = bind$4(store, prop);                      
38726                         }
38727                 });
38728                 each$1(storages, function(storage) {
38729                         store._addStorage(storage);
38730                 });
38731                 each$1(plugins, function(plugin) {
38732                         store._addPlugin(plugin);
38733                 });
38734                 return store
38735         }
38736
38737         var Global$1 = util.Global;
38738
38739         var localStorage_1 = {
38740                 name: 'localStorage',
38741                 read: read,
38742                 write: write,
38743                 each: each$2,
38744                 remove: remove$2,
38745                 clearAll: clearAll,
38746         };
38747
38748         function localStorage$1() {
38749                 return Global$1.localStorage
38750         }
38751
38752         function read(key) {
38753                 return localStorage$1().getItem(key)
38754         }
38755
38756         function write(key, data) {
38757                 return localStorage$1().setItem(key, data)
38758         }
38759
38760         function each$2(fn) {
38761                 for (var i = localStorage$1().length - 1; i >= 0; i--) {
38762                         var key = localStorage$1().key(i);
38763                         fn(read(key), key);
38764                 }
38765         }
38766
38767         function remove$2(key) {
38768                 return localStorage$1().removeItem(key)
38769         }
38770
38771         function clearAll() {
38772                 return localStorage$1().clear()
38773         }
38774
38775         // oldFF-globalStorage provides storage for Firefox
38776         // versions 6 and 7, where no localStorage, etc
38777         // is available.
38778
38779
38780         var Global$2 = util.Global;
38781
38782         var oldFFGlobalStorage = {
38783                 name: 'oldFF-globalStorage',
38784                 read: read$1,
38785                 write: write$1,
38786                 each: each$3,
38787                 remove: remove$3,
38788                 clearAll: clearAll$1,
38789         };
38790
38791         var globalStorage = Global$2.globalStorage;
38792
38793         function read$1(key) {
38794                 return globalStorage[key]
38795         }
38796
38797         function write$1(key, data) {
38798                 globalStorage[key] = data;
38799         }
38800
38801         function each$3(fn) {
38802                 for (var i = globalStorage.length - 1; i >= 0; i--) {
38803                         var key = globalStorage.key(i);
38804                         fn(globalStorage[key], key);
38805                 }
38806         }
38807
38808         function remove$3(key) {
38809                 return globalStorage.removeItem(key)
38810         }
38811
38812         function clearAll$1() {
38813                 each$3(function(key, _) {
38814                         delete globalStorage[key];
38815                 });
38816         }
38817
38818         // oldIE-userDataStorage provides storage for Internet Explorer
38819         // versions 6 and 7, where no localStorage, sessionStorage, etc
38820         // is available.
38821
38822
38823         var Global$3 = util.Global;
38824
38825         var oldIEUserDataStorage = {
38826                 name: 'oldIE-userDataStorage',
38827                 write: write$2,
38828                 read: read$2,
38829                 each: each$4,
38830                 remove: remove$4,
38831                 clearAll: clearAll$2,
38832         };
38833
38834         var storageName = 'storejs';
38835         var doc = Global$3.document;
38836         var _withStorageEl = _makeIEStorageElFunction();
38837         var disable = (Global$3.navigator ? Global$3.navigator.userAgent : '').match(/ (MSIE 8|MSIE 9|MSIE 10)\./); // MSIE 9.x, MSIE 10.x
38838
38839         function write$2(unfixedKey, data) {
38840                 if (disable) { return }
38841                 var fixedKey = fixKey(unfixedKey);
38842                 _withStorageEl(function(storageEl) {
38843                         storageEl.setAttribute(fixedKey, data);
38844                         storageEl.save(storageName);
38845                 });
38846         }
38847
38848         function read$2(unfixedKey) {
38849                 if (disable) { return }
38850                 var fixedKey = fixKey(unfixedKey);
38851                 var res = null;
38852                 _withStorageEl(function(storageEl) {
38853                         res = storageEl.getAttribute(fixedKey);
38854                 });
38855                 return res
38856         }
38857
38858         function each$4(callback) {
38859                 _withStorageEl(function(storageEl) {
38860                         var attributes = storageEl.XMLDocument.documentElement.attributes;
38861                         for (var i=attributes.length-1; i>=0; i--) {
38862                                 var attr = attributes[i];
38863                                 callback(storageEl.getAttribute(attr.name), attr.name);
38864                         }
38865                 });
38866         }
38867
38868         function remove$4(unfixedKey) {
38869                 var fixedKey = fixKey(unfixedKey);
38870                 _withStorageEl(function(storageEl) {
38871                         storageEl.removeAttribute(fixedKey);
38872                         storageEl.save(storageName);
38873                 });
38874         }
38875
38876         function clearAll$2() {
38877                 _withStorageEl(function(storageEl) {
38878                         var attributes = storageEl.XMLDocument.documentElement.attributes;
38879                         storageEl.load(storageName);
38880                         for (var i=attributes.length-1; i>=0; i--) {
38881                                 storageEl.removeAttribute(attributes[i].name);
38882                         }
38883                         storageEl.save(storageName);
38884                 });
38885         }
38886
38887         // Helpers
38888         //////////
38889
38890         // In IE7, keys cannot start with a digit or contain certain chars.
38891         // See https://github.com/marcuswestin/store.js/issues/40
38892         // See https://github.com/marcuswestin/store.js/issues/83
38893         var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g");
38894         function fixKey(key) {
38895                 return key.replace(/^\d/, '___$&').replace(forbiddenCharsRegex, '___')
38896         }
38897
38898         function _makeIEStorageElFunction() {
38899                 if (!doc || !doc.documentElement || !doc.documentElement.addBehavior) {
38900                         return null
38901                 }
38902                 var scriptTag = 'script',
38903                         storageOwner,
38904                         storageContainer,
38905                         storageEl;
38906
38907                 // Since #userData storage applies only to specific paths, we need to
38908                 // somehow link our data to a specific path.  We choose /favicon.ico
38909                 // as a pretty safe option, since all browsers already make a request to
38910                 // this URL anyway and being a 404 will not hurt us here.  We wrap an
38911                 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
38912                 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
38913                 // since the iframe access rules appear to allow direct access and
38914                 // manipulation of the document element, even for a 404 page.  This
38915                 // document can be used instead of the current document (which would
38916                 // have been limited to the current path) to perform #userData storage.
38917                 try {
38918                         /* global ActiveXObject */
38919                         storageContainer = new ActiveXObject('htmlfile');
38920                         storageContainer.open();
38921                         storageContainer.write('<'+scriptTag+'>document.w=window</'+scriptTag+'><iframe src="/favicon.ico"></iframe>');
38922                         storageContainer.close();
38923                         storageOwner = storageContainer.w.frames[0].document;
38924                         storageEl = storageOwner.createElement('div');
38925                 } catch(e) {
38926                         // somehow ActiveXObject instantiation failed (perhaps some special
38927                         // security settings or otherwse), fall back to per-path storage
38928                         storageEl = doc.createElement('div');
38929                         storageOwner = doc.body;
38930                 }
38931
38932                 return function(storeFunction) {
38933                         var args = [].slice.call(arguments, 0);
38934                         args.unshift(storageEl);
38935                         // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
38936                         // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
38937                         storageOwner.appendChild(storageEl);
38938                         storageEl.addBehavior('#default#userData');
38939                         storageEl.load(storageName);
38940                         storeFunction.apply(this, args);
38941                         storageOwner.removeChild(storageEl);
38942                         return
38943                 }
38944         }
38945
38946         // cookieStorage is useful Safari private browser mode, where localStorage
38947         // doesn't work but cookies do. This implementation is adopted from
38948         // https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage
38949
38950
38951         var Global$4 = util.Global;
38952         var trim$1 = util.trim;
38953
38954         var cookieStorage = {
38955                 name: 'cookieStorage',
38956                 read: read$3,
38957                 write: write$3,
38958                 each: each$5,
38959                 remove: remove$5,
38960                 clearAll: clearAll$3,
38961         };
38962
38963         var doc$1 = Global$4.document;
38964
38965         function read$3(key) {
38966                 if (!key || !_has(key)) { return null }
38967                 var regexpStr = "(?:^|.*;\\s*)" +
38968                         escape(key).replace(/[\-\.\+\*]/g, "\\$&") +
38969                         "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*";
38970                 return unescape(doc$1.cookie.replace(new RegExp(regexpStr), "$1"))
38971         }
38972
38973         function each$5(callback) {
38974                 var cookies = doc$1.cookie.split(/; ?/g);
38975                 for (var i = cookies.length - 1; i >= 0; i--) {
38976                         if (!trim$1(cookies[i])) {
38977                                 continue
38978                         }
38979                         var kvp = cookies[i].split('=');
38980                         var key = unescape(kvp[0]);
38981                         var val = unescape(kvp[1]);
38982                         callback(val, key);
38983                 }
38984         }
38985
38986         function write$3(key, data) {
38987                 if(!key) { return }
38988                 doc$1.cookie = escape(key) + "=" + escape(data) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/";
38989         }
38990
38991         function remove$5(key) {
38992                 if (!key || !_has(key)) {
38993                         return
38994                 }
38995                 doc$1.cookie = escape(key) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
38996         }
38997
38998         function clearAll$3() {
38999                 each$5(function(_, key) {
39000                         remove$5(key);
39001                 });
39002         }
39003
39004         function _has(key) {
39005                 return (new RegExp("(?:^|;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(doc$1.cookie)
39006         }
39007
39008         var Global$5 = util.Global;
39009
39010         var sessionStorage_1 = {
39011                 name: 'sessionStorage',
39012                 read: read$4,
39013                 write: write$4,
39014                 each: each$6,
39015                 remove: remove$6,
39016                 clearAll: clearAll$4
39017         };
39018
39019         function sessionStorage() {
39020                 return Global$5.sessionStorage
39021         }
39022
39023         function read$4(key) {
39024                 return sessionStorage().getItem(key)
39025         }
39026
39027         function write$4(key, data) {
39028                 return sessionStorage().setItem(key, data)
39029         }
39030
39031         function each$6(fn) {
39032                 for (var i = sessionStorage().length - 1; i >= 0; i--) {
39033                         var key = sessionStorage().key(i);
39034                         fn(read$4(key), key);
39035                 }
39036         }
39037
39038         function remove$6(key) {
39039                 return sessionStorage().removeItem(key)
39040         }
39041
39042         function clearAll$4() {
39043                 return sessionStorage().clear()
39044         }
39045
39046         // memoryStorage is a useful last fallback to ensure that the store
39047         // is functions (meaning store.get(), store.set(), etc will all function).
39048         // However, stored values will not persist when the browser navigates to
39049         // a new page or reloads the current page.
39050
39051         var memoryStorage_1 = {
39052                 name: 'memoryStorage',
39053                 read: read$5,
39054                 write: write$5,
39055                 each: each$7,
39056                 remove: remove$7,
39057                 clearAll: clearAll$5,
39058         };
39059
39060         var memoryStorage = {};
39061
39062         function read$5(key) {
39063                 return memoryStorage[key]
39064         }
39065
39066         function write$5(key, data) {
39067                 memoryStorage[key] = data;
39068         }
39069
39070         function each$7(callback) {
39071                 for (var key in memoryStorage) {
39072                         if (memoryStorage.hasOwnProperty(key)) {
39073                                 callback(memoryStorage[key], key);
39074                         }
39075                 }
39076         }
39077
39078         function remove$7(key) {
39079                 delete memoryStorage[key];
39080         }
39081
39082         function clearAll$5(key) {
39083                 memoryStorage = {};
39084         }
39085
39086         var all = [
39087                 // Listed in order of usage preference
39088                 localStorage_1,
39089                 oldFFGlobalStorage,
39090                 oldIEUserDataStorage,
39091                 cookieStorage,
39092                 sessionStorage_1,
39093                 memoryStorage_1
39094         ];
39095
39096         /* eslint-disable */
39097
39098         //  json2.js
39099         //  2016-10-28
39100         //  Public Domain.
39101         //  NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
39102         //  See http://www.JSON.org/js.html
39103         //  This code should be minified before deployment.
39104         //  See http://javascript.crockford.com/jsmin.html
39105
39106         //  USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
39107         //  NOT CONTROL.
39108
39109         //  This file creates a global JSON object containing two methods: stringify
39110         //  and parse. This file provides the ES5 JSON capability to ES3 systems.
39111         //  If a project might run on IE8 or earlier, then this file should be included.
39112         //  This file does nothing on ES5 systems.
39113
39114         //      JSON.stringify(value, replacer, space)
39115         //          value       any JavaScript value, usually an object or array.
39116         //          replacer    an optional parameter that determines how object
39117         //                      values are stringified for objects. It can be a
39118         //                      function or an array of strings.
39119         //          space       an optional parameter that specifies the indentation
39120         //                      of nested structures. If it is omitted, the text will
39121         //                      be packed without extra whitespace. If it is a number,
39122         //                      it will specify the number of spaces to indent at each
39123         //                      level. If it is a string (such as "\t" or "&nbsp;"),
39124         //                      it contains the characters used to indent at each level.
39125         //          This method produces a JSON text from a JavaScript value.
39126         //          When an object value is found, if the object contains a toJSON
39127         //          method, its toJSON method will be called and the result will be
39128         //          stringified. A toJSON method does not serialize: it returns the
39129         //          value represented by the name/value pair that should be serialized,
39130         //          or undefined if nothing should be serialized. The toJSON method
39131         //          will be passed the key associated with the value, and this will be
39132         //          bound to the value.
39133
39134         //          For example, this would serialize Dates as ISO strings.
39135
39136         //              Date.prototype.toJSON = function (key) {
39137         //                  function f(n) {
39138         //                      // Format integers to have at least two digits.
39139         //                      return (n < 10)
39140         //                          ? "0" + n
39141         //                          : n;
39142         //                  }
39143         //                  return this.getUTCFullYear()   + "-" +
39144         //                       f(this.getUTCMonth() + 1) + "-" +
39145         //                       f(this.getUTCDate())      + "T" +
39146         //                       f(this.getUTCHours())     + ":" +
39147         //                       f(this.getUTCMinutes())   + ":" +
39148         //                       f(this.getUTCSeconds())   + "Z";
39149         //              };
39150
39151         //          You can provide an optional replacer method. It will be passed the
39152         //          key and value of each member, with this bound to the containing
39153         //          object. The value that is returned from your method will be
39154         //          serialized. If your method returns undefined, then the member will
39155         //          be excluded from the serialization.
39156
39157         //          If the replacer parameter is an array of strings, then it will be
39158         //          used to select the members to be serialized. It filters the results
39159         //          such that only members with keys listed in the replacer array are
39160         //          stringified.
39161
39162         //          Values that do not have JSON representations, such as undefined or
39163         //          functions, will not be serialized. Such values in objects will be
39164         //          dropped; in arrays they will be replaced with null. You can use
39165         //          a replacer function to replace those with JSON values.
39166
39167         //          JSON.stringify(undefined) returns undefined.
39168
39169         //          The optional space parameter produces a stringification of the
39170         //          value that is filled with line breaks and indentation to make it
39171         //          easier to read.
39172
39173         //          If the space parameter is a non-empty string, then that string will
39174         //          be used for indentation. If the space parameter is a number, then
39175         //          the indentation will be that many spaces.
39176
39177         //          Example:
39178
39179         //          text = JSON.stringify(["e", {pluribus: "unum"}]);
39180         //          // text is '["e",{"pluribus":"unum"}]'
39181
39182         //          text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t");
39183         //          // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
39184
39185         //          text = JSON.stringify([new Date()], function (key, value) {
39186         //              return this[key] instanceof Date
39187         //                  ? "Date(" + this[key] + ")"
39188         //                  : value;
39189         //          });
39190         //          // text is '["Date(---current time---)"]'
39191
39192         //      JSON.parse(text, reviver)
39193         //          This method parses a JSON text to produce an object or array.
39194         //          It can throw a SyntaxError exception.
39195
39196         //          The optional reviver parameter is a function that can filter and
39197         //          transform the results. It receives each of the keys and values,
39198         //          and its return value is used instead of the original value.
39199         //          If it returns what it received, then the structure is not modified.
39200         //          If it returns undefined then the member is deleted.
39201
39202         //          Example:
39203
39204         //          // Parse the text. Values that look like ISO date strings will
39205         //          // be converted to Date objects.
39206
39207         //          myData = JSON.parse(text, function (key, value) {
39208         //              var a;
39209         //              if (typeof value === "string") {
39210         //                  a =
39211         //   /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
39212         //                  if (a) {
39213         //                      return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
39214         //                          +a[5], +a[6]));
39215         //                  }
39216         //              }
39217         //              return value;
39218         //          });
39219
39220         //          myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
39221         //              var d;
39222         //              if (typeof value === "string" &&
39223         //                      value.slice(0, 5) === "Date(" &&
39224         //                      value.slice(-1) === ")") {
39225         //                  d = new Date(value.slice(5, -1));
39226         //                  if (d) {
39227         //                      return d;
39228         //                  }
39229         //              }
39230         //              return value;
39231         //          });
39232
39233         //  This is a reference implementation. You are free to copy, modify, or
39234         //  redistribute.
39235
39236         /*jslint
39237             eval, for, this
39238         */
39239
39240         /*property
39241             JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
39242             getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
39243             lastIndex, length, parse, prototype, push, replace, slice, stringify,
39244             test, toJSON, toString, valueOf
39245         */
39246
39247
39248         // Create a JSON object only if one does not already exist. We create the
39249         // methods in a closure to avoid creating global variables.
39250
39251         if (typeof JSON !== "object") {
39252             JSON = {};
39253         }
39254
39255         (function () {
39256
39257             var rx_one = /^[\],:{}\s]*$/;
39258             var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
39259             var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
39260             var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
39261             var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
39262             var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
39263
39264             function f(n) {
39265                 // Format integers to have at least two digits.
39266                 return n < 10
39267                     ? "0" + n
39268                     : n;
39269             }
39270
39271             function this_value() {
39272                 return this.valueOf();
39273             }
39274
39275             if (typeof Date.prototype.toJSON !== "function") {
39276
39277                 Date.prototype.toJSON = function () {
39278
39279                     return isFinite(this.valueOf())
39280                         ? this.getUTCFullYear() + "-" +
39281                                 f(this.getUTCMonth() + 1) + "-" +
39282                                 f(this.getUTCDate()) + "T" +
39283                                 f(this.getUTCHours()) + ":" +
39284                                 f(this.getUTCMinutes()) + ":" +
39285                                 f(this.getUTCSeconds()) + "Z"
39286                         : null;
39287                 };
39288
39289                 Boolean.prototype.toJSON = this_value;
39290                 Number.prototype.toJSON = this_value;
39291                 String.prototype.toJSON = this_value;
39292             }
39293
39294             var gap;
39295             var indent;
39296             var meta;
39297             var rep;
39298
39299
39300             function quote(string) {
39301
39302         // If the string contains no control characters, no quote characters, and no
39303         // backslash characters, then we can safely slap some quotes around it.
39304         // Otherwise we must also replace the offending characters with safe escape
39305         // sequences.
39306
39307                 rx_escapable.lastIndex = 0;
39308                 return rx_escapable.test(string)
39309                     ? "\"" + string.replace(rx_escapable, function (a) {
39310                         var c = meta[a];
39311                         return typeof c === "string"
39312                             ? c
39313                             : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
39314                     }) + "\""
39315                     : "\"" + string + "\"";
39316             }
39317
39318
39319             function str(key, holder) {
39320
39321         // Produce a string from holder[key].
39322
39323                 var i;          // The loop counter.
39324                 var k;          // The member key.
39325                 var v;          // The member value.
39326                 var length;
39327                 var mind = gap;
39328                 var partial;
39329                 var value = holder[key];
39330
39331         // If the value has a toJSON method, call it to obtain a replacement value.
39332
39333                 if (value && typeof value === "object" &&
39334                         typeof value.toJSON === "function") {
39335                     value = value.toJSON(key);
39336                 }
39337
39338         // If we were called with a replacer function, then call the replacer to
39339         // obtain a replacement value.
39340
39341                 if (typeof rep === "function") {
39342                     value = rep.call(holder, key, value);
39343                 }
39344
39345         // What happens next depends on the value's type.
39346
39347                 switch (typeof value) {
39348                 case "string":
39349                     return quote(value);
39350
39351                 case "number":
39352
39353         // JSON numbers must be finite. Encode non-finite numbers as null.
39354
39355                     return isFinite(value)
39356                         ? String(value)
39357                         : "null";
39358
39359                 case "boolean":
39360                 case "null":
39361
39362         // If the value is a boolean or null, convert it to a string. Note:
39363         // typeof null does not produce "null". The case is included here in
39364         // the remote chance that this gets fixed someday.
39365
39366                     return String(value);
39367
39368         // If the type is "object", we might be dealing with an object or an array or
39369         // null.
39370
39371                 case "object":
39372
39373         // Due to a specification blunder in ECMAScript, typeof null is "object",
39374         // so watch out for that case.
39375
39376                     if (!value) {
39377                         return "null";
39378                     }
39379
39380         // Make an array to hold the partial results of stringifying this object value.
39381
39382                     gap += indent;
39383                     partial = [];
39384
39385         // Is the value an array?
39386
39387                     if (Object.prototype.toString.apply(value) === "[object Array]") {
39388
39389         // The value is an array. Stringify every element. Use null as a placeholder
39390         // for non-JSON values.
39391
39392                         length = value.length;
39393                         for (i = 0; i < length; i += 1) {
39394                             partial[i] = str(i, value) || "null";
39395                         }
39396
39397         // Join all of the elements together, separated with commas, and wrap them in
39398         // brackets.
39399
39400                         v = partial.length === 0
39401                             ? "[]"
39402                             : gap
39403                                 ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]"
39404                                 : "[" + partial.join(",") + "]";
39405                         gap = mind;
39406                         return v;
39407                     }
39408
39409         // If the replacer is an array, use it to select the members to be stringified.
39410
39411                     if (rep && typeof rep === "object") {
39412                         length = rep.length;
39413                         for (i = 0; i < length; i += 1) {
39414                             if (typeof rep[i] === "string") {
39415                                 k = rep[i];
39416                                 v = str(k, value);
39417                                 if (v) {
39418                                     partial.push(quote(k) + (
39419                                         gap
39420                                             ? ": "
39421                                             : ":"
39422                                     ) + v);
39423                                 }
39424                             }
39425                         }
39426                     } else {
39427
39428         // Otherwise, iterate through all of the keys in the object.
39429
39430                         for (k in value) {
39431                             if (Object.prototype.hasOwnProperty.call(value, k)) {
39432                                 v = str(k, value);
39433                                 if (v) {
39434                                     partial.push(quote(k) + (
39435                                         gap
39436                                             ? ": "
39437                                             : ":"
39438                                     ) + v);
39439                                 }
39440                             }
39441                         }
39442                     }
39443
39444         // Join all of the member texts together, separated with commas,
39445         // and wrap them in braces.
39446
39447                     v = partial.length === 0
39448                         ? "{}"
39449                         : gap
39450                             ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}"
39451                             : "{" + partial.join(",") + "}";
39452                     gap = mind;
39453                     return v;
39454                 }
39455             }
39456
39457         // If the JSON object does not yet have a stringify method, give it one.
39458
39459             if (typeof JSON.stringify !== "function") {
39460                 meta = {    // table of character substitutions
39461                     "\b": "\\b",
39462                     "\t": "\\t",
39463                     "\n": "\\n",
39464                     "\f": "\\f",
39465                     "\r": "\\r",
39466                     "\"": "\\\"",
39467                     "\\": "\\\\"
39468                 };
39469                 JSON.stringify = function (value, replacer, space) {
39470
39471         // The stringify method takes a value and an optional replacer, and an optional
39472         // space parameter, and returns a JSON text. The replacer can be a function
39473         // that can replace values, or an array of strings that will select the keys.
39474         // A default replacer method can be provided. Use of the space parameter can
39475         // produce text that is more easily readable.
39476
39477                     var i;
39478                     gap = "";
39479                     indent = "";
39480
39481         // If the space parameter is a number, make an indent string containing that
39482         // many spaces.
39483
39484                     if (typeof space === "number") {
39485                         for (i = 0; i < space; i += 1) {
39486                             indent += " ";
39487                         }
39488
39489         // If the space parameter is a string, it will be used as the indent string.
39490
39491                     } else if (typeof space === "string") {
39492                         indent = space;
39493                     }
39494
39495         // If there is a replacer, it must be a function or an array.
39496         // Otherwise, throw an error.
39497
39498                     rep = replacer;
39499                     if (replacer && typeof replacer !== "function" &&
39500                             (typeof replacer !== "object" ||
39501                             typeof replacer.length !== "number")) {
39502                         throw new Error("JSON.stringify");
39503                     }
39504
39505         // Make a fake root object containing our value under the key of "".
39506         // Return the result of stringifying the value.
39507
39508                     return str("", {"": value});
39509                 };
39510             }
39511
39512
39513         // If the JSON object does not yet have a parse method, give it one.
39514
39515             if (typeof JSON.parse !== "function") {
39516                 JSON.parse = function (text, reviver) {
39517
39518         // The parse method takes a text and an optional reviver function, and returns
39519         // a JavaScript value if the text is a valid JSON text.
39520
39521                     var j;
39522
39523                     function walk(holder, key) {
39524
39525         // The walk method is used to recursively walk the resulting structure so
39526         // that modifications can be made.
39527
39528                         var k;
39529                         var v;
39530                         var value = holder[key];
39531                         if (value && typeof value === "object") {
39532                             for (k in value) {
39533                                 if (Object.prototype.hasOwnProperty.call(value, k)) {
39534                                     v = walk(value, k);
39535                                     if (v !== undefined) {
39536                                         value[k] = v;
39537                                     } else {
39538                                         delete value[k];
39539                                     }
39540                                 }
39541                             }
39542                         }
39543                         return reviver.call(holder, key, value);
39544                     }
39545
39546
39547         // Parsing happens in four stages. In the first stage, we replace certain
39548         // Unicode characters with escape sequences. JavaScript handles many characters
39549         // incorrectly, either silently deleting them, or treating them as line endings.
39550
39551                     text = String(text);
39552                     rx_dangerous.lastIndex = 0;
39553                     if (rx_dangerous.test(text)) {
39554                         text = text.replace(rx_dangerous, function (a) {
39555                             return "\\u" +
39556                                     ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
39557                         });
39558                     }
39559
39560         // In the second stage, we run the text against regular expressions that look
39561         // for non-JSON patterns. We are especially concerned with "()" and "new"
39562         // because they can cause invocation, and "=" because it can cause mutation.
39563         // But just to be safe, we want to reject all unexpected forms.
39564
39565         // We split the second stage into 4 regexp operations in order to work around
39566         // crippling inefficiencies in IE's and Safari's regexp engines. First we
39567         // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
39568         // replace all simple value tokens with "]" characters. Third, we delete all
39569         // open brackets that follow a colon or comma or that begin the text. Finally,
39570         // we look to see that the remaining characters are only whitespace or "]" or
39571         // "," or ":" or "{" or "}". If that is so, then the text is safe for eval.
39572
39573                     if (
39574                         rx_one.test(
39575                             text
39576                                 .replace(rx_two, "@")
39577                                 .replace(rx_three, "]")
39578                                 .replace(rx_four, "")
39579                         )
39580                     ) {
39581
39582         // In the third stage we use the eval function to compile the text into a
39583         // JavaScript structure. The "{" operator is subject to a syntactic ambiguity
39584         // in JavaScript: it can begin a block or an object literal. We wrap the text
39585         // in parens to eliminate the ambiguity.
39586
39587                         j = eval("(" + text + ")");
39588
39589         // In the optional fourth stage, we recursively walk the new structure, passing
39590         // each name/value pair to a reviver function for possible transformation.
39591
39592                         return (typeof reviver === "function")
39593                             ? walk({"": j}, "")
39594                             : j;
39595                     }
39596
39597         // If the text is not JSON parseable, then a SyntaxError is thrown.
39598
39599                     throw new SyntaxError("JSON.parse");
39600                 };
39601             }
39602         }());
39603
39604         var json2 = json2Plugin;
39605
39606         function json2Plugin() {
39607                 
39608                 return {}
39609         }
39610
39611         var plugins = [json2];
39612
39613         var store_legacy = storeEngine.createStore(all, plugins);
39614
39615         // # osm-auth
39616         //
39617         // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
39618         // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
39619         // does not support custom headers, which this uses everywhere.
39620         var osmAuth = function(o) {
39621
39622             var oauth = {};
39623
39624             // authenticated users will also have a request token secret, but it's
39625             // not used in transactions with the server
39626             oauth.authenticated = function() {
39627                 return !!(token('oauth_token') && token('oauth_token_secret'));
39628             };
39629
39630             oauth.logout = function() {
39631                 token('oauth_token', '');
39632                 token('oauth_token_secret', '');
39633                 token('oauth_request_token_secret', '');
39634                 return oauth;
39635             };
39636
39637             // TODO: detect lack of click event
39638             oauth.authenticate = function(callback) {
39639                 if (oauth.authenticated()) return callback();
39640
39641                 oauth.logout();
39642
39643                 // ## Getting a request token
39644                 var params = timenonce(getAuth(o)),
39645                     url = o.url + '/oauth/request_token';
39646
39647                 params.oauth_signature = ohauth_1.signature(
39648                     o.oauth_secret, '',
39649                     ohauth_1.baseString('POST', url, params));
39650
39651                 if (!o.singlepage) {
39652                     // Create a 600x550 popup window in the center of the screen
39653                     var w = 600, h = 550,
39654                         settings = [
39655                             ['width', w], ['height', h],
39656                             ['left', screen.width / 2 - w / 2],
39657                             ['top', screen.height / 2 - h / 2]].map(function(x) {
39658                                 return x.join('=');
39659                             }).join(','),
39660                         popup = window.open('about:blank', 'oauth_window', settings);
39661                 }
39662
39663                 // Request a request token. When this is complete, the popup
39664                 // window is redirected to OSM's authorization page.
39665                 ohauth_1.xhr('POST', url, params, null, {}, reqTokenDone);
39666                 o.loading();
39667
39668                 function reqTokenDone(err, xhr) {
39669                     o.done();
39670                     if (err) return callback(err);
39671                     var resp = ohauth_1.stringQs(xhr.response);
39672                     token('oauth_request_token_secret', resp.oauth_token_secret);
39673                     var authorize_url = o.url + '/oauth/authorize?' + ohauth_1.qsString({
39674                         oauth_token: resp.oauth_token,
39675                         oauth_callback: resolveUrl$1(o.landing)
39676                     });
39677
39678                     if (o.singlepage) {
39679                         location.href = authorize_url;
39680                     } else {
39681                         popup.location = authorize_url;
39682                     }
39683                 }
39684
39685                 // Called by a function in a landing page, in the popup window. The
39686                 // window closes itself.
39687                 window.authComplete = function(token) {
39688                     var oauth_token = ohauth_1.stringQs(token.split('?')[1]);
39689                     get_access_token(oauth_token.oauth_token);
39690                     delete window.authComplete;
39691                 };
39692
39693                 // ## Getting an request token
39694                 //
39695                 // At this point we have an `oauth_token`, brought in from a function
39696                 // call on a landing page popup.
39697                 function get_access_token(oauth_token) {
39698                     var url = o.url + '/oauth/access_token',
39699                         params = timenonce(getAuth(o)),
39700                         request_token_secret = token('oauth_request_token_secret');
39701                     params.oauth_token = oauth_token;
39702                     params.oauth_signature = ohauth_1.signature(
39703                         o.oauth_secret,
39704                         request_token_secret,
39705                         ohauth_1.baseString('POST', url, params));
39706
39707                     // ## Getting an access token
39708                     //
39709                     // The final token required for authentication. At this point
39710                     // we have a `request token secret`
39711                     ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
39712                     o.loading();
39713                 }
39714
39715                 function accessTokenDone(err, xhr) {
39716                     o.done();
39717                     if (err) return callback(err);
39718                     var access_token = ohauth_1.stringQs(xhr.response);
39719                     token('oauth_token', access_token.oauth_token);
39720                     token('oauth_token_secret', access_token.oauth_token_secret);
39721                     callback(null, oauth);
39722                 }
39723             };
39724
39725             oauth.bootstrapToken = function(oauth_token, callback) {
39726                 // ## Getting an request token
39727                 // At this point we have an `oauth_token`, brought in from a function
39728                 // call on a landing page popup.
39729                 function get_access_token(oauth_token) {
39730                     var url = o.url + '/oauth/access_token',
39731                         params = timenonce(getAuth(o)),
39732                         request_token_secret = token('oauth_request_token_secret');
39733                     params.oauth_token = oauth_token;
39734                     params.oauth_signature = ohauth_1.signature(
39735                         o.oauth_secret,
39736                         request_token_secret,
39737                         ohauth_1.baseString('POST', url, params));
39738
39739                     // ## Getting an access token
39740                     // The final token required for authentication. At this point
39741                     // we have a `request token secret`
39742                     ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
39743                     o.loading();
39744                 }
39745
39746                 function accessTokenDone(err, xhr) {
39747                     o.done();
39748                     if (err) return callback(err);
39749                     var access_token = ohauth_1.stringQs(xhr.response);
39750                     token('oauth_token', access_token.oauth_token);
39751                     token('oauth_token_secret', access_token.oauth_token_secret);
39752                     callback(null, oauth);
39753                 }
39754
39755                 get_access_token(oauth_token);
39756             };
39757
39758             // # xhr
39759             //
39760             // A single XMLHttpRequest wrapper that does authenticated calls if the
39761             // user has logged in.
39762             oauth.xhr = function(options, callback) {
39763                 if (!oauth.authenticated()) {
39764                     if (o.auto) {
39765                         return oauth.authenticate(run);
39766                     } else {
39767                         callback('not authenticated', null);
39768                         return;
39769                     }
39770                 } else {
39771                     return run();
39772                 }
39773
39774                 function run() {
39775                     var params = timenonce(getAuth(o)),
39776                         oauth_token_secret = token('oauth_token_secret'),
39777                         url = (options.prefix !== false) ? o.url + options.path : options.path,
39778                         url_parts = url.replace(/#.*$/, '').split('?', 2),
39779                         base_url = url_parts[0],
39780                         query = (url_parts.length === 2) ? url_parts[1] : '';
39781
39782                     // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
39783                     if ((!options.options || !options.options.header ||
39784                         options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') &&
39785                         options.content) {
39786                         params = immutable(params, ohauth_1.stringQs(options.content));
39787                     }
39788
39789                     params.oauth_token = token('oauth_token');
39790                     params.oauth_signature = ohauth_1.signature(
39791                         o.oauth_secret,
39792                         oauth_token_secret,
39793                         ohauth_1.baseString(options.method, base_url, immutable(params, ohauth_1.stringQs(query)))
39794                     );
39795
39796                     return ohauth_1.xhr(options.method, url, params, options.content, options.options, done);
39797                 }
39798
39799                 function done(err, xhr) {
39800                     if (err) return callback(err);
39801                     else if (xhr.responseXML) return callback(err, xhr.responseXML);
39802                     else return callback(err, xhr.response);
39803                 }
39804             };
39805
39806             // pre-authorize this object, if we can just get a token and token_secret
39807             // from the start
39808             oauth.preauth = function(c) {
39809                 if (!c) return;
39810                 if (c.oauth_token) token('oauth_token', c.oauth_token);
39811                 if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
39812                 return oauth;
39813             };
39814
39815             oauth.options = function(_) {
39816                 if (!arguments.length) return o;
39817
39818                 o = _;
39819                 o.url = o.url || 'https://www.openstreetmap.org';
39820                 o.landing = o.landing || 'land.html';
39821                 o.singlepage = o.singlepage || false;
39822
39823                 // Optional loading and loading-done functions for nice UI feedback.
39824                 // by default, no-ops
39825                 o.loading = o.loading || function() {};
39826                 o.done = o.done || function() {};
39827
39828                 return oauth.preauth(o);
39829             };
39830
39831             // 'stamp' an authentication object from `getAuth()`
39832             // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
39833             // and timestamp
39834             function timenonce(o) {
39835                 o.oauth_timestamp = ohauth_1.timestamp();
39836                 o.oauth_nonce = ohauth_1.nonce();
39837                 return o;
39838             }
39839
39840             // get/set tokens. These are prefixed with the base URL so that `osm-auth`
39841             // can be used with multiple APIs and the keys in `localStorage`
39842             // will not clash
39843             var token;
39844
39845             if (store_legacy.enabled) {
39846                 token = function (x, y) {
39847                     if (arguments.length === 1) return store_legacy.get(o.url + x);
39848                     else if (arguments.length === 2) return store_legacy.set(o.url + x, y);
39849                 };
39850             } else {
39851                 var storage = {};
39852                 token = function (x, y) {
39853                     if (arguments.length === 1) return storage[o.url + x];
39854                     else if (arguments.length === 2) return storage[o.url + x] = y;
39855                 };
39856             }
39857
39858             // Get an authentication object. If you just add and remove properties
39859             // from a single object, you'll need to use `delete` to make sure that
39860             // it doesn't contain undesired properties for authentication
39861             function getAuth(o) {
39862                 return {
39863                     oauth_consumer_key: o.oauth_consumer_key,
39864                     oauth_signature_method: 'HMAC-SHA1'
39865                 };
39866             }
39867
39868             // potentially pre-authorize
39869             oauth.options(o);
39870
39871             return oauth;
39872         };
39873
39874         var JXON = new (function () {
39875           var
39876             sValueProp = 'keyValue', sAttributesProp = 'keyAttributes', sAttrPref = '@', /* you can customize these values */
39877             aCache = [], rIsNull = /^\s*$/, rIsBool = /^(?:true|false)$/i;
39878
39879           function parseText (sValue) {
39880             if (rIsNull.test(sValue)) { return null; }
39881             if (rIsBool.test(sValue)) { return sValue.toLowerCase() === 'true'; }
39882             if (isFinite(sValue)) { return parseFloat(sValue); }
39883             if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
39884             return sValue;
39885           }
39886
39887           function EmptyTree () { }
39888           EmptyTree.prototype.toString = function () { return 'null'; };
39889           EmptyTree.prototype.valueOf = function () { return null; };
39890
39891           function objectify (vValue) {
39892             return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
39893           }
39894
39895           function createObjTree (oParentNode, nVerb, bFreeze, bNesteAttr) {
39896             var
39897               nLevelStart = aCache.length, bChildren = oParentNode.hasChildNodes(),
39898               bAttributes = oParentNode.hasAttributes(), bHighVerb = Boolean(nVerb & 2);
39899
39900             var
39901               sProp, vContent, nLength = 0, sCollectedTxt = '',
39902               vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true;
39903
39904             if (bChildren) {
39905               for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
39906                 oNode = oParentNode.childNodes.item(nItem);
39907                 if (oNode.nodeType === 4) { sCollectedTxt += oNode.nodeValue; } /* nodeType is 'CDATASection' (4) */
39908                 else if (oNode.nodeType === 3) { sCollectedTxt += oNode.nodeValue.trim(); } /* nodeType is 'Text' (3) */
39909                 else if (oNode.nodeType === 1 && !oNode.prefix) { aCache.push(oNode); } /* nodeType is 'Element' (1) */
39910               }
39911             }
39912
39913             var nLevelEnd = aCache.length, vBuiltVal = parseText(sCollectedTxt);
39914
39915             if (!bHighVerb && (bChildren || bAttributes)) { vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; }
39916
39917             for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
39918               sProp = aCache[nElId].nodeName.toLowerCase();
39919               vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
39920               if (vResult.hasOwnProperty(sProp)) {
39921                 if (vResult[sProp].constructor !== Array) { vResult[sProp] = [vResult[sProp]]; }
39922                 vResult[sProp].push(vContent);
39923               } else {
39924                 vResult[sProp] = vContent;
39925                 nLength++;
39926               }
39927             }
39928
39929             if (bAttributes) {
39930               var
39931                 nAttrLen = oParentNode.attributes.length,
39932                 sAPrefix = bNesteAttr ? '' : sAttrPref, oAttrParent = bNesteAttr ? {} : vResult;
39933
39934               for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
39935                 oAttrib = oParentNode.attributes.item(nAttrib);
39936                 oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
39937               }
39938
39939               if (bNesteAttr) {
39940                 if (bFreeze) { Object.freeze(oAttrParent); }
39941                 vResult[sAttributesProp] = oAttrParent;
39942                 nLength -= nAttrLen - 1;
39943               }
39944             }
39945
39946             if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
39947               vResult[sValueProp] = vBuiltVal;
39948             } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
39949               vResult = vBuiltVal;
39950             }
39951
39952             if (bFreeze && (bHighVerb || nLength > 0)) { Object.freeze(vResult); }
39953
39954             aCache.length = nLevelStart;
39955
39956             return vResult;
39957           }
39958
39959           function loadObjTree (oXMLDoc, oParentEl, oParentObj) {
39960             var vValue, oChild;
39961
39962             if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
39963               oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */
39964             } else if (oParentObj.constructor === Date) {
39965               oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));    
39966             }
39967
39968             for (var sName in oParentObj) {
39969               vValue = oParentObj[sName];
39970               if (isFinite(sName) || vValue instanceof Function) { continue; } /* verbosity level is 0 */
39971               if (sName === sValueProp) {
39972                 if (vValue !== null && vValue !== true) { oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); }
39973               } else if (sName === sAttributesProp) { /* verbosity level is 3 */
39974                 for (var sAttrib in vValue) { oParentEl.setAttribute(sAttrib, vValue[sAttrib]); }
39975               } else if (sName.charAt(0) === sAttrPref) {
39976                 oParentEl.setAttribute(sName.slice(1), vValue);
39977               } else if (vValue.constructor === Array) {
39978                 for (var nItem = 0; nItem < vValue.length; nItem++) {
39979                   oChild = oXMLDoc.createElement(sName);
39980                   loadObjTree(oXMLDoc, oChild, vValue[nItem]);
39981                   oParentEl.appendChild(oChild);
39982                 }
39983               } else {
39984                 oChild = oXMLDoc.createElement(sName);
39985                 if (vValue instanceof Object) {
39986                   loadObjTree(oXMLDoc, oChild, vValue);
39987                 } else if (vValue !== null && vValue !== true) {
39988                   oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
39989                 }
39990                 oParentEl.appendChild(oChild);
39991              }
39992            }
39993           }
39994
39995           this.build = function (oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) {
39996             var _nVerb = arguments.length > 1 && typeof nVerbosity === 'number' ? nVerbosity & 3 : /* put here the default verbosity level: */ 1;
39997             return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);    
39998           };
39999
40000           this.unbuild = function (oObjTree) {    
40001             var oNewDoc = document.implementation.createDocument('', '', null);
40002             loadObjTree(oNewDoc, oNewDoc, oObjTree);
40003             return oNewDoc;
40004           };
40005
40006           this.stringify = function (oObjTree) {
40007             return (new XMLSerializer()).serializeToString(JXON.unbuild(oObjTree));
40008           };
40009         })();
40010
40011         // var myObject = JXON.build(doc);
40012         // we got our javascript object! try: alert(JSON.stringify(myObject));
40013
40014         // var newDoc = JXON.unbuild(myObject);
40015         // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
40016
40017         var tiler$5 = utilTiler();
40018         var dispatch$6 = dispatch('apiStatusChange', 'authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedNotes');
40019         var urlroot = 'https://www.openstreetmap.org';
40020         var oauth = osmAuth({
40021             url: urlroot,
40022             oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
40023             oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
40024             loading: authLoading,
40025             done: authDone
40026         });
40027
40028         var _blacklists = ['.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*'];
40029         var _tileCache = { toLoad: {}, loaded: {}, inflight: {}, seen: {}, rtree: new RBush() };
40030         var _noteCache = { toLoad: {}, loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: new RBush() };
40031         var _userCache = { toLoad: {}, user: {} };
40032         var _cachedApiStatus;
40033         var _changeset = {};
40034
40035         var _deferred = new Set();
40036         var _connectionID = 1;
40037         var _tileZoom$3 = 16;
40038         var _noteZoom = 12;
40039         var _rateLimitError;
40040         var _userChangesets;
40041         var _userDetails;
40042         var _off;
40043
40044         // set a default but also load this from the API status
40045         var _maxWayNodes = 2000;
40046
40047
40048         function authLoading() {
40049             dispatch$6.call('authLoading');
40050         }
40051
40052
40053         function authDone() {
40054             dispatch$6.call('authDone');
40055         }
40056
40057
40058         function abortRequest$5(controllerOrXHR) {
40059             if (controllerOrXHR) {
40060                 controllerOrXHR.abort();
40061             }
40062         }
40063
40064
40065         function hasInflightRequests(cache) {
40066             return Object.keys(cache.inflight).length;
40067         }
40068
40069
40070         function abortUnwantedRequests$3(cache, visibleTiles) {
40071             Object.keys(cache.inflight).forEach(function(k) {
40072                 if (cache.toLoad[k]) return;
40073                 if (visibleTiles.find(function(tile) { return k === tile.id; })) return;
40074
40075                 abortRequest$5(cache.inflight[k]);
40076                 delete cache.inflight[k];
40077             });
40078         }
40079
40080
40081         function getLoc(attrs) {
40082             var lon = attrs.lon && attrs.lon.value;
40083             var lat = attrs.lat && attrs.lat.value;
40084             return [parseFloat(lon), parseFloat(lat)];
40085         }
40086
40087
40088         function getNodes(obj) {
40089             var elems = obj.getElementsByTagName('nd');
40090             var nodes = new Array(elems.length);
40091             for (var i = 0, l = elems.length; i < l; i++) {
40092                 nodes[i] = 'n' + elems[i].attributes.ref.value;
40093             }
40094             return nodes;
40095         }
40096
40097         function getNodesJSON(obj) {
40098             var elems = obj.nodes;
40099             var nodes = new Array(elems.length);
40100             for (var i = 0, l = elems.length; i < l; i++) {
40101                 nodes[i] = 'n' + elems[i];
40102             }
40103             return nodes;
40104         }
40105
40106         function getTags(obj) {
40107             var elems = obj.getElementsByTagName('tag');
40108             var tags = {};
40109             for (var i = 0, l = elems.length; i < l; i++) {
40110                 var attrs = elems[i].attributes;
40111                 tags[attrs.k.value] = attrs.v.value;
40112             }
40113
40114             return tags;
40115         }
40116
40117
40118         function getMembers(obj) {
40119             var elems = obj.getElementsByTagName('member');
40120             var members = new Array(elems.length);
40121             for (var i = 0, l = elems.length; i < l; i++) {
40122                 var attrs = elems[i].attributes;
40123                 members[i] = {
40124                     id: attrs.type.value[0] + attrs.ref.value,
40125                     type: attrs.type.value,
40126                     role: attrs.role.value
40127                 };
40128             }
40129             return members;
40130         }
40131
40132         function getMembersJSON(obj) {
40133             var elems = obj.members;
40134             var members = new Array(elems.length);
40135             for (var i = 0, l = elems.length; i < l; i++) {
40136                 var attrs = elems[i];
40137                 members[i] = {
40138                     id: attrs.type[0] + attrs.ref,
40139                     type: attrs.type,
40140                     role: attrs.role
40141                 };
40142             }
40143             return members;
40144         }
40145
40146         function getVisible(attrs) {
40147             return (!attrs.visible || attrs.visible.value !== 'false');
40148         }
40149
40150
40151         function parseComments(comments) {
40152             var parsedComments = [];
40153
40154             // for each comment
40155             for (var i = 0; i < comments.length; i++) {
40156                 var comment = comments[i];
40157                 if (comment.nodeName === 'comment') {
40158                     var childNodes = comment.childNodes;
40159                     var parsedComment = {};
40160
40161                     for (var j = 0; j < childNodes.length; j++) {
40162                         var node = childNodes[j];
40163                         var nodeName = node.nodeName;
40164                         if (nodeName === '#text') continue;
40165                         parsedComment[nodeName] = node.textContent;
40166
40167                         if (nodeName === 'uid') {
40168                             var uid = node.textContent;
40169                             if (uid && !_userCache.user[uid]) {
40170                                 _userCache.toLoad[uid] = true;
40171                             }
40172                         }
40173                     }
40174
40175                     if (parsedComment) {
40176                         parsedComments.push(parsedComment);
40177                     }
40178                 }
40179             }
40180             return parsedComments;
40181         }
40182
40183
40184         function encodeNoteRtree(note) {
40185             return {
40186                 minX: note.loc[0],
40187                 minY: note.loc[1],
40188                 maxX: note.loc[0],
40189                 maxY: note.loc[1],
40190                 data: note
40191             };
40192         }
40193
40194
40195         var jsonparsers = {
40196
40197             node: function nodeData(obj, uid) {
40198                 return new osmNode({
40199                     id:  uid,
40200                     visible: typeof obj.visible === 'boolean' ? obj.visible : true,
40201                     version: obj.version.toString(),
40202                     changeset: obj.changeset.toString(),
40203                     timestamp: obj.timestamp,
40204                     user: obj.user,
40205                     uid: obj.uid.toString(),
40206                     loc: [parseFloat(obj.lon), parseFloat(obj.lat)],
40207                     tags: obj.tags
40208                 });
40209             },
40210
40211             way: function wayData(obj, uid) {
40212                 return new osmWay({
40213                     id:  uid,
40214                     visible: typeof obj.visible === 'boolean' ? obj.visible : true,
40215                     version: obj.version.toString(),
40216                     changeset: obj.changeset.toString(),
40217                     timestamp: obj.timestamp,
40218                     user: obj.user,
40219                     uid: obj.uid.toString(),
40220                     tags: obj.tags,
40221                     nodes: getNodesJSON(obj)
40222                 });
40223             },
40224
40225             relation: function relationData(obj, uid) {
40226                 return new osmRelation({
40227                     id:  uid,
40228                     visible: typeof obj.visible === 'boolean' ? obj.visible : true,
40229                     version: obj.version.toString(),
40230                     changeset: obj.changeset.toString(),
40231                     timestamp: obj.timestamp,
40232                     user: obj.user,
40233                     uid: obj.uid.toString(),
40234                     tags: obj.tags,
40235                     members: getMembersJSON(obj)
40236                 });
40237             }
40238         };
40239
40240         function parseJSON(payload, callback, options) {
40241             options = Object.assign({ skipSeen: true }, options);
40242             if (!payload)  {
40243                 return callback({ message: 'No JSON', status: -1 });
40244             }
40245
40246             var json = payload;
40247             if (typeof json !== 'object')
40248                json = JSON.parse(payload);
40249
40250             if (!json.elements)
40251                 return callback({ message: 'No JSON', status: -1 });
40252
40253             var children = json.elements;
40254
40255             var handle = window.requestIdleCallback(function() {
40256                 var results = [];
40257                 var result;
40258                 for (var i = 0; i < children.length; i++) {
40259                     result = parseChild(children[i]);
40260                     if (result) results.push(result);
40261                 }
40262                 callback(null, results);
40263             });
40264
40265             _deferred.add(handle);
40266
40267             function parseChild(child) {
40268                 var parser = jsonparsers[child.type];
40269                 if (!parser) return null;
40270
40271                 var uid;
40272
40273                 uid = osmEntity.id.fromOSM(child.type, child.id);
40274                 if (options.skipSeen) {
40275                     if (_tileCache.seen[uid]) return null;  // avoid reparsing a "seen" entity
40276                     _tileCache.seen[uid] = true;
40277                 }
40278
40279                 return parser(child, uid);
40280             }
40281         }
40282
40283         var parsers = {
40284             node: function nodeData(obj, uid) {
40285                 var attrs = obj.attributes;
40286                 return new osmNode({
40287                     id: uid,
40288                     visible: getVisible(attrs),
40289                     version: attrs.version.value,
40290                     changeset: attrs.changeset && attrs.changeset.value,
40291                     timestamp: attrs.timestamp && attrs.timestamp.value,
40292                     user: attrs.user && attrs.user.value,
40293                     uid: attrs.uid && attrs.uid.value,
40294                     loc: getLoc(attrs),
40295                     tags: getTags(obj)
40296                 });
40297             },
40298
40299             way: function wayData(obj, uid) {
40300                 var attrs = obj.attributes;
40301                 return new osmWay({
40302                     id: uid,
40303                     visible: getVisible(attrs),
40304                     version: attrs.version.value,
40305                     changeset: attrs.changeset && attrs.changeset.value,
40306                     timestamp: attrs.timestamp && attrs.timestamp.value,
40307                     user: attrs.user && attrs.user.value,
40308                     uid: attrs.uid && attrs.uid.value,
40309                     tags: getTags(obj),
40310                     nodes: getNodes(obj),
40311                 });
40312             },
40313
40314             relation: function relationData(obj, uid) {
40315                 var attrs = obj.attributes;
40316                 return new osmRelation({
40317                     id: uid,
40318                     visible: getVisible(attrs),
40319                     version: attrs.version.value,
40320                     changeset: attrs.changeset && attrs.changeset.value,
40321                     timestamp: attrs.timestamp && attrs.timestamp.value,
40322                     user: attrs.user && attrs.user.value,
40323                     uid: attrs.uid && attrs.uid.value,
40324                     tags: getTags(obj),
40325                     members: getMembers(obj)
40326                 });
40327             },
40328
40329             note: function parseNote(obj, uid) {
40330                 var attrs = obj.attributes;
40331                 var childNodes = obj.childNodes;
40332                 var props = {};
40333
40334                 props.id = uid;
40335                 props.loc = getLoc(attrs);
40336
40337                 // if notes are coincident, move them apart slightly
40338                 var coincident = false;
40339                 var epsilon = 0.00001;
40340                 do {
40341                     if (coincident) {
40342                         props.loc = geoVecAdd(props.loc, [epsilon, epsilon]);
40343                     }
40344                     var bbox = geoExtent(props.loc).bbox();
40345                     coincident = _noteCache.rtree.search(bbox).length;
40346                 } while (coincident);
40347
40348                 // parse note contents
40349                 for (var i = 0; i < childNodes.length; i++) {
40350                     var node = childNodes[i];
40351                     var nodeName = node.nodeName;
40352                     if (nodeName === '#text') continue;
40353
40354                     // if the element is comments, parse the comments
40355                     if (nodeName === 'comments') {
40356                         props[nodeName] = parseComments(node.childNodes);
40357                     } else {
40358                         props[nodeName] = node.textContent;
40359                     }
40360                 }
40361
40362                 var note = new osmNote(props);
40363                 var item = encodeNoteRtree(note);
40364                 _noteCache.note[note.id] = note;
40365                 _noteCache.rtree.insert(item);
40366
40367                 return note;
40368             },
40369
40370             user: function parseUser(obj, uid) {
40371                 var attrs = obj.attributes;
40372                 var user = {
40373                     id: uid,
40374                     display_name: attrs.display_name && attrs.display_name.value,
40375                     account_created: attrs.account_created && attrs.account_created.value,
40376                     changesets_count: '0',
40377                     active_blocks: '0'
40378                 };
40379
40380                 var img = obj.getElementsByTagName('img');
40381                 if (img && img[0] && img[0].getAttribute('href')) {
40382                     user.image_url = img[0].getAttribute('href');
40383                 }
40384
40385                 var changesets = obj.getElementsByTagName('changesets');
40386                 if (changesets && changesets[0] && changesets[0].getAttribute('count')) {
40387                     user.changesets_count = changesets[0].getAttribute('count');
40388                 }
40389
40390                 var blocks = obj.getElementsByTagName('blocks');
40391                 if (blocks && blocks[0]) {
40392                     var received = blocks[0].getElementsByTagName('received');
40393                     if (received && received[0] && received[0].getAttribute('active')) {
40394                         user.active_blocks = received[0].getAttribute('active');
40395                     }
40396                 }
40397
40398                 _userCache.user[uid] = user;
40399                 delete _userCache.toLoad[uid];
40400                 return user;
40401             }
40402         };
40403
40404
40405         function parseXML(xml, callback, options) {
40406             options = Object.assign({ skipSeen: true }, options);
40407             if (!xml || !xml.childNodes) {
40408                 return callback({ message: 'No XML', status: -1 });
40409             }
40410
40411             var root = xml.childNodes[0];
40412             var children = root.childNodes;
40413
40414             var handle = window.requestIdleCallback(function() {
40415                 var results = [];
40416                 var result;
40417                 for (var i = 0; i < children.length; i++) {
40418                     result = parseChild(children[i]);
40419                     if (result) results.push(result);
40420                 }
40421                 callback(null, results);
40422             });
40423
40424             _deferred.add(handle);
40425
40426
40427             function parseChild(child) {
40428                 var parser = parsers[child.nodeName];
40429                 if (!parser) return null;
40430
40431                 var uid;
40432                 if (child.nodeName === 'user') {
40433                     uid = child.attributes.id.value;
40434                     if (options.skipSeen && _userCache.user[uid]) {
40435                         delete _userCache.toLoad[uid];
40436                         return null;
40437                     }
40438
40439                 } else if (child.nodeName === 'note') {
40440                     uid = child.getElementsByTagName('id')[0].textContent;
40441
40442                 } else {
40443                     uid = osmEntity.id.fromOSM(child.nodeName, child.attributes.id.value);
40444                     if (options.skipSeen) {
40445                         if (_tileCache.seen[uid]) return null;  // avoid reparsing a "seen" entity
40446                         _tileCache.seen[uid] = true;
40447                     }
40448                 }
40449
40450                 return parser(child, uid);
40451             }
40452         }
40453
40454
40455         // replace or remove note from rtree
40456         function updateRtree$3(item, replace) {
40457             _noteCache.rtree.remove(item, function isEql(a, b) { return a.data.id === b.data.id; });
40458
40459             if (replace) {
40460                 _noteCache.rtree.insert(item);
40461             }
40462         }
40463
40464
40465         function wrapcb(thisArg, callback, cid) {
40466             return function(err, result) {
40467                 if (err) {
40468                     // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
40469                     if (err.status === 400 || err.status === 401 || err.status === 403) {
40470                         thisArg.logout();
40471                     }
40472                     return callback.call(thisArg, err);
40473
40474                 } else if (thisArg.getConnectionId() !== cid) {
40475                     return callback.call(thisArg, { message: 'Connection Switched', status: -1 });
40476
40477                 } else {
40478                     return callback.call(thisArg, err, result);
40479                 }
40480             };
40481         }
40482
40483
40484         var serviceOsm = {
40485
40486             init: function() {
40487                 utilRebind(this, dispatch$6, 'on');
40488             },
40489
40490
40491             reset: function() {
40492                 Array.from(_deferred).forEach(function(handle) {
40493                     window.cancelIdleCallback(handle);
40494                     _deferred.delete(handle);
40495                 });
40496
40497                 _connectionID++;
40498                 _userChangesets = undefined;
40499                 _userDetails = undefined;
40500                 _rateLimitError = undefined;
40501
40502                 Object.values(_tileCache.inflight).forEach(abortRequest$5);
40503                 Object.values(_noteCache.inflight).forEach(abortRequest$5);
40504                 Object.values(_noteCache.inflightPost).forEach(abortRequest$5);
40505                 if (_changeset.inflight) abortRequest$5(_changeset.inflight);
40506
40507                 _tileCache = { toLoad: {}, loaded: {}, inflight: {}, seen: {}, rtree: new RBush() };
40508                 _noteCache = { toLoad: {}, loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: new RBush() };
40509                 _userCache = { toLoad: {}, user: {} };
40510                 _cachedApiStatus = undefined;
40511                 _changeset = {};
40512
40513                 return this;
40514             },
40515
40516
40517             getConnectionId: function() {
40518                 return _connectionID;
40519             },
40520
40521
40522             changesetURL: function(changesetID) {
40523                 return urlroot + '/changeset/' + changesetID;
40524             },
40525
40526
40527             changesetsURL: function(center, zoom) {
40528                 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
40529                 return urlroot + '/history#map=' +
40530                     Math.floor(zoom) + '/' +
40531                     center[1].toFixed(precision) + '/' +
40532                     center[0].toFixed(precision);
40533             },
40534
40535
40536             entityURL: function(entity) {
40537                 return urlroot + '/' + entity.type + '/' + entity.osmId();
40538             },
40539
40540
40541             historyURL: function(entity) {
40542                 return urlroot + '/' + entity.type + '/' + entity.osmId() + '/history';
40543             },
40544
40545
40546             userURL: function(username) {
40547                 return urlroot + '/user/' + username;
40548             },
40549
40550
40551             noteURL: function(note) {
40552                 return urlroot + '/note/' + note.id;
40553             },
40554
40555
40556             noteReportURL: function(note) {
40557                 return urlroot + '/reports/new?reportable_type=Note&reportable_id=' + note.id;
40558             },
40559
40560
40561             // Generic method to load data from the OSM API
40562             // Can handle either auth or unauth calls.
40563             loadFromAPI: function(path, callback, options) {
40564                 options = Object.assign({ skipSeen: true }, options);
40565                 var that = this;
40566                 var cid = _connectionID;
40567
40568                 function done(err, payload) {
40569                     if (that.getConnectionId() !== cid) {
40570                         if (callback) callback({ message: 'Connection Switched', status: -1 });
40571                         return;
40572                     }
40573
40574                     var isAuthenticated = that.authenticated();
40575
40576                     // 400 Bad Request, 401 Unauthorized, 403 Forbidden
40577                     // Logout and retry the request..
40578                     if (isAuthenticated && err && err.status &&
40579                             (err.status === 400 || err.status === 401 || err.status === 403)) {
40580                         that.logout();
40581                         that.loadFromAPI(path, callback, options);
40582
40583                     // else, no retry..
40584                     } else {
40585                         // 509 Bandwidth Limit Exceeded, 429 Too Many Requests
40586                         // Set the rateLimitError flag and trigger a warning..
40587                         if (!isAuthenticated && !_rateLimitError && err && err.status &&
40588                                 (err.status === 509 || err.status === 429)) {
40589                             _rateLimitError = err;
40590                             dispatch$6.call('change');
40591                             that.reloadApiStatus();
40592
40593                         } else if ((err && _cachedApiStatus === 'online') ||
40594                             (!err && _cachedApiStatus !== 'online')) {
40595                             // If the response's error state doesn't match the status,
40596                             // it's likely we lost or gained the connection so reload the status
40597                             that.reloadApiStatus();
40598                         }
40599
40600                         if (callback) {
40601                             if (err) {
40602                                 return callback(err);
40603                             } else {
40604                                 if (path.indexOf('.json') !== -1) {
40605                                     return parseJSON(payload, callback, options);
40606                                 } else {
40607                                     return parseXML(payload, callback, options);
40608                                 }
40609                             }
40610                         }
40611                     }
40612                 }
40613
40614                 if (this.authenticated()) {
40615                     return oauth.xhr({ method: 'GET', path: path }, done);
40616                 } else {
40617                     var url = urlroot + path;
40618                     var controller = new AbortController();
40619                     d3_json(url, { signal: controller.signal })
40620                         .then(function(data) {
40621                             done(null, data);
40622                         })
40623                         .catch(function(err) {
40624                             if (err.name === 'AbortError') return;
40625                             // d3-fetch includes status in the error message,
40626                             // but we can't access the response itself
40627                             // https://github.com/d3/d3-fetch/issues/27
40628                             var match = err.message.match(/^\d{3}/);
40629                             if (match) {
40630                                 done({ status: +match[0], statusText: err.message });
40631                             } else {
40632                                 done(err.message);
40633                             }
40634                         });
40635                     return controller;
40636                 }
40637             },
40638
40639
40640             // Load a single entity by id (ways and relations use the `/full` call)
40641             // GET /api/0.6/node/#id
40642             // GET /api/0.6/[way|relation]/#id/full
40643             loadEntity: function(id, callback) {
40644                 var type = osmEntity.id.type(id);
40645                 var osmID = osmEntity.id.toOSM(id);
40646                 var options = { skipSeen: false };
40647
40648                 this.loadFromAPI(
40649                     '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : '') + '.json',
40650                     function(err, entities) {
40651                         if (callback) callback(err, { data: entities });
40652                     },
40653                     options
40654                 );
40655             },
40656
40657
40658             // Load a single entity with a specific version
40659             // GET /api/0.6/[node|way|relation]/#id/#version
40660             loadEntityVersion: function(id, version, callback) {
40661                 var type = osmEntity.id.type(id);
40662                 var osmID = osmEntity.id.toOSM(id);
40663                 var options = { skipSeen: false };
40664
40665                 this.loadFromAPI(
40666                     '/api/0.6/' + type + '/' + osmID + '/' + version + '.json',
40667                     function(err, entities) {
40668                         if (callback) callback(err, { data: entities });
40669                     },
40670                     options
40671                 );
40672             },
40673
40674
40675             // Load multiple entities in chunks
40676             // (note: callback may be called multiple times)
40677             // Unlike `loadEntity`, child nodes and members are not fetched
40678             // GET /api/0.6/[nodes|ways|relations]?#parameters
40679             loadMultiple: function(ids, callback) {
40680                 var that = this;
40681                 var groups = utilArrayGroupBy(utilArrayUniq(ids), osmEntity.id.type);
40682
40683                 Object.keys(groups).forEach(function(k) {
40684                     var type = k + 's';   // nodes, ways, relations
40685                     var osmIDs = groups[k].map(function(id) { return osmEntity.id.toOSM(id); });
40686                     var options = { skipSeen: false };
40687
40688                     utilArrayChunk(osmIDs, 150).forEach(function(arr) {
40689                         that.loadFromAPI(
40690                             '/api/0.6/' + type + '.json?' + type + '=' + arr.join(),
40691                             function(err, entities) {
40692                                 if (callback) callback(err, { data: entities });
40693                             },
40694                             options
40695                         );
40696                     });
40697                 });
40698             },
40699
40700
40701             // Create, upload, and close a changeset
40702             // PUT /api/0.6/changeset/create
40703             // POST /api/0.6/changeset/#id/upload
40704             // PUT /api/0.6/changeset/#id/close
40705             putChangeset: function(changeset, changes, callback) {
40706                 var cid = _connectionID;
40707
40708                 if (_changeset.inflight) {
40709                     return callback({ message: 'Changeset already inflight', status: -2 }, changeset);
40710
40711                 } else if (_changeset.open) {   // reuse existing open changeset..
40712                     return createdChangeset.call(this, null, _changeset.open);
40713
40714                 } else {   // Open a new changeset..
40715                     var options = {
40716                         method: 'PUT',
40717                         path: '/api/0.6/changeset/create',
40718                         options: { header: { 'Content-Type': 'text/xml' } },
40719                         content: JXON.stringify(changeset.asJXON())
40720                     };
40721                     _changeset.inflight = oauth.xhr(
40722                         options,
40723                         wrapcb(this, createdChangeset, cid)
40724                     );
40725                 }
40726
40727
40728                 function createdChangeset(err, changesetID) {
40729                     _changeset.inflight = null;
40730                     if (err) { return callback(err, changeset); }
40731
40732                     _changeset.open = changesetID;
40733                     changeset = changeset.update({ id: changesetID });
40734
40735                     // Upload the changeset..
40736                     var options = {
40737                         method: 'POST',
40738                         path: '/api/0.6/changeset/' + changesetID + '/upload',
40739                         options: { header: { 'Content-Type': 'text/xml' } },
40740                         content: JXON.stringify(changeset.osmChangeJXON(changes))
40741                     };
40742                     _changeset.inflight = oauth.xhr(
40743                         options,
40744                         wrapcb(this, uploadedChangeset, cid)
40745                     );
40746                 }
40747
40748
40749                 function uploadedChangeset(err) {
40750                     _changeset.inflight = null;
40751                     if (err) return callback(err, changeset);
40752
40753                     // Upload was successful, safe to call the callback.
40754                     // Add delay to allow for postgres replication #1646 #2678
40755                     window.setTimeout(function() { callback(null, changeset); }, 2500);
40756                     _changeset.open = null;
40757
40758                     // At this point, we don't really care if the connection was switched..
40759                     // Only try to close the changeset if we're still talking to the same server.
40760                     if (this.getConnectionId() === cid) {
40761                         // Still attempt to close changeset, but ignore response because #2667
40762                         oauth.xhr({
40763                             method: 'PUT',
40764                             path: '/api/0.6/changeset/' + changeset.id + '/close',
40765                             options: { header: { 'Content-Type': 'text/xml' } }
40766                         }, function() { return true; });
40767                     }
40768                 }
40769             },
40770
40771
40772             // Load multiple users in chunks
40773             // (note: callback may be called multiple times)
40774             // GET /api/0.6/users?users=#id1,#id2,...,#idn
40775             loadUsers: function(uids, callback) {
40776                 var toLoad = [];
40777                 var cached = [];
40778
40779                 utilArrayUniq(uids).forEach(function(uid) {
40780                     if (_userCache.user[uid]) {
40781                         delete _userCache.toLoad[uid];
40782                         cached.push(_userCache.user[uid]);
40783                     } else {
40784                         toLoad.push(uid);
40785                     }
40786                 });
40787
40788                 if (cached.length || !this.authenticated()) {
40789                     callback(undefined, cached);
40790                     if (!this.authenticated()) return;  // require auth
40791                 }
40792
40793                 utilArrayChunk(toLoad, 150).forEach(function(arr) {
40794                     oauth.xhr(
40795                         { method: 'GET', path: '/api/0.6/users?users=' + arr.join() },
40796                         wrapcb(this, done, _connectionID)
40797                     );
40798                 }.bind(this));
40799
40800                 function done(err, xml) {
40801                     if (err) { return callback(err); }
40802
40803                     var options = { skipSeen: true };
40804                     return parseXML(xml, function(err, results) {
40805                         if (err) {
40806                             return callback(err);
40807                         } else {
40808                             return callback(undefined, results);
40809                         }
40810                     }, options);
40811                 }
40812             },
40813
40814
40815             // Load a given user by id
40816             // GET /api/0.6/user/#id
40817             loadUser: function(uid, callback) {
40818                 if (_userCache.user[uid] || !this.authenticated()) {   // require auth
40819                     delete _userCache.toLoad[uid];
40820                     return callback(undefined, _userCache.user[uid]);
40821                 }
40822
40823                 oauth.xhr(
40824                     { method: 'GET', path: '/api/0.6/user/' + uid },
40825                     wrapcb(this, done, _connectionID)
40826                 );
40827
40828                 function done(err, xml) {
40829                     if (err) { return callback(err); }
40830
40831                     var options = { skipSeen: true };
40832                     return parseXML(xml, function(err, results) {
40833                         if (err) {
40834                             return callback(err);
40835                         } else {
40836                             return callback(undefined, results[0]);
40837                         }
40838                     }, options);
40839                 }
40840             },
40841
40842
40843             // Load the details of the logged-in user
40844             // GET /api/0.6/user/details
40845             userDetails: function(callback) {
40846                 if (_userDetails) {    // retrieve cached
40847                     return callback(undefined, _userDetails);
40848                 }
40849
40850                 oauth.xhr(
40851                     { method: 'GET', path: '/api/0.6/user/details' },
40852                     wrapcb(this, done, _connectionID)
40853                 );
40854
40855                 function done(err, xml) {
40856                     if (err) { return callback(err); }
40857
40858                     var options = { skipSeen: false };
40859                     return parseXML(xml, function(err, results) {
40860                         if (err) {
40861                             return callback(err);
40862                         } else {
40863                             _userDetails = results[0];
40864                             return callback(undefined, _userDetails);
40865                         }
40866                     }, options);
40867                 }
40868             },
40869
40870
40871             // Load previous changesets for the logged in user
40872             // GET /api/0.6/changesets?user=#id
40873             userChangesets: function(callback) {
40874                 if (_userChangesets) {    // retrieve cached
40875                     return callback(undefined, _userChangesets);
40876                 }
40877
40878                 this.userDetails(
40879                     wrapcb(this, gotDetails, _connectionID)
40880                 );
40881
40882
40883                 function gotDetails(err, user) {
40884                     if (err) { return callback(err); }
40885
40886                     oauth.xhr(
40887                         { method: 'GET', path: '/api/0.6/changesets?user=' + user.id },
40888                         wrapcb(this, done, _connectionID)
40889                     );
40890                 }
40891
40892                 function done(err, xml) {
40893                     if (err) { return callback(err); }
40894
40895                     _userChangesets = Array.prototype.map.call(
40896                         xml.getElementsByTagName('changeset'),
40897                         function (changeset) { return { tags: getTags(changeset) }; }
40898                     ).filter(function (changeset) {
40899                         var comment = changeset.tags.comment;
40900                         return comment && comment !== '';
40901                     });
40902
40903                     return callback(undefined, _userChangesets);
40904                 }
40905             },
40906
40907
40908             // Fetch the status of the OSM API
40909             // GET /api/capabilities
40910             status: function(callback) {
40911                 var url = urlroot + '/api/capabilities';
40912                 var errback = wrapcb(this, done, _connectionID);
40913                 d3_xml(url)
40914                     .then(function(data) { errback(null, data); })
40915                     .catch(function(err) { errback(err.message); });
40916
40917                 function done(err, xml) {
40918                     if (err) {
40919                         // the status is null if no response could be retrieved
40920                         return callback(err, null);
40921                     }
40922
40923                     // update blacklists
40924                     var elements = xml.getElementsByTagName('blacklist');
40925                     var regexes = [];
40926                     for (var i = 0; i < elements.length; i++) {
40927                         var regex = elements[i].getAttribute('regex');  // needs unencode?
40928                         if (regex) {
40929                             regexes.push(regex);
40930                         }
40931                     }
40932                     if (regexes.length) {
40933                         _blacklists = regexes;
40934                     }
40935
40936                     if (_rateLimitError) {
40937                         return callback(_rateLimitError, 'rateLimited');
40938                     } else {
40939                         var waynodes = xml.getElementsByTagName('waynodes');
40940                         var maxWayNodes = waynodes.length && parseInt(waynodes[0].getAttribute('maximum'), 10);
40941                         if (maxWayNodes && isFinite(maxWayNodes)) _maxWayNodes = maxWayNodes;
40942
40943                         var apiStatus = xml.getElementsByTagName('status');
40944                         var val = apiStatus[0].getAttribute('api');
40945                         return callback(undefined, val);
40946                     }
40947                 }
40948             },
40949
40950             // Calls `status` and dispatches an `apiStatusChange` event if the returned
40951             // status differs from the cached status.
40952             reloadApiStatus: function() {
40953                 // throttle to avoid unncessary API calls
40954                 if (!this.throttledReloadApiStatus) {
40955                     var that = this;
40956                     this.throttledReloadApiStatus = throttle(function() {
40957                         that.status(function(err, status) {
40958                             if (status !== _cachedApiStatus) {
40959                                 _cachedApiStatus = status;
40960                                 dispatch$6.call('apiStatusChange', that, err, status);
40961                             }
40962                         });
40963                     }, 500);
40964                 }
40965                 this.throttledReloadApiStatus();
40966             },
40967
40968
40969             // Returns the maximum number of nodes a single way can have
40970             maxWayNodes: function() {
40971                 return _maxWayNodes;
40972             },
40973
40974
40975             // Load data (entities) from the API in tiles
40976             // GET /api/0.6/map?bbox=
40977             loadTiles: function(projection, callback) {
40978                 if (_off) return;
40979
40980                 // determine the needed tiles to cover the view
40981                 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection);
40982
40983                 // abort inflight requests that are no longer needed
40984                 var hadRequests = hasInflightRequests(_tileCache);
40985                 abortUnwantedRequests$3(_tileCache, tiles);
40986                 if (hadRequests && !hasInflightRequests(_tileCache)) {
40987                     dispatch$6.call('loaded');    // stop the spinner
40988                 }
40989
40990                 // issue new requests..
40991                 tiles.forEach(function(tile) {
40992                     this.loadTile(tile, callback);
40993                 }, this);
40994             },
40995
40996
40997             // Load a single data tile
40998             // GET /api/0.6/map?bbox=
40999             loadTile: function(tile, callback) {
41000                 if (_off) return;
41001                 if (_tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
41002
41003                 if (!hasInflightRequests(_tileCache)) {
41004                     dispatch$6.call('loading');   // start the spinner
41005                 }
41006
41007                 var path = '/api/0.6/map.json?bbox=';
41008                 var options = { skipSeen: true };
41009
41010                 _tileCache.inflight[tile.id] = this.loadFromAPI(
41011                     path + tile.extent.toParam(),
41012                     tileCallback,
41013                     options
41014                 );
41015
41016                 function tileCallback(err, parsed) {
41017                     delete _tileCache.inflight[tile.id];
41018                     if (!err) {
41019                         delete _tileCache.toLoad[tile.id];
41020                         _tileCache.loaded[tile.id] = true;
41021                         var bbox = tile.extent.bbox();
41022                         bbox.id = tile.id;
41023                         _tileCache.rtree.insert(bbox);
41024                     }
41025                     if (callback) {
41026                         callback(err, Object.assign({ data: parsed }, tile));
41027                     }
41028                     if (!hasInflightRequests(_tileCache)) {
41029                         dispatch$6.call('loaded');     // stop the spinner
41030                     }
41031                 }
41032             },
41033
41034
41035             isDataLoaded: function(loc) {
41036                 var bbox = { minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1] };
41037                 return _tileCache.rtree.collides(bbox);
41038             },
41039
41040
41041             // load the tile that covers the given `loc`
41042             loadTileAtLoc: function(loc, callback) {
41043                 // Back off if the toLoad queue is filling up.. re #6417
41044                 // (Currently `loadTileAtLoc` requests are considered low priority - used by operations to
41045                 // let users safely edit geometries which extend to unloaded tiles.  We can drop some.)
41046                 if (Object.keys(_tileCache.toLoad).length > 50) return;
41047
41048                 var k = geoZoomToScale(_tileZoom$3 + 1);
41049                 var offset = geoRawMercator().scale(k)(loc);
41050                 var projection = geoRawMercator().transform({ k: k, x: -offset[0], y: -offset[1] });
41051                 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection);
41052
41053                 tiles.forEach(function(tile) {
41054                     if (_tileCache.toLoad[tile.id] || _tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
41055
41056                     _tileCache.toLoad[tile.id] = true;
41057                     this.loadTile(tile, callback);
41058                 }, this);
41059             },
41060
41061
41062             // Load notes from the API in tiles
41063             // GET /api/0.6/notes?bbox=
41064             loadNotes: function(projection, noteOptions) {
41065                 noteOptions = Object.assign({ limit: 10000, closed: 7 }, noteOptions);
41066                 if (_off) return;
41067
41068                 var that = this;
41069                 var path = '/api/0.6/notes?limit=' + noteOptions.limit + '&closed=' + noteOptions.closed + '&bbox=';
41070                 var throttleLoadUsers = throttle(function() {
41071                     var uids = Object.keys(_userCache.toLoad);
41072                     if (!uids.length) return;
41073                     that.loadUsers(uids, function() {});  // eagerly load user details
41074                 }, 750);
41075
41076                 // determine the needed tiles to cover the view
41077                 var tiles = tiler$5.zoomExtent([_noteZoom, _noteZoom]).getTiles(projection);
41078
41079                 // abort inflight requests that are no longer needed
41080                 abortUnwantedRequests$3(_noteCache, tiles);
41081
41082                 // issue new requests..
41083                 tiles.forEach(function(tile) {
41084                     if (_noteCache.loaded[tile.id] || _noteCache.inflight[tile.id]) return;
41085
41086                     var options = { skipSeen: false };
41087                     _noteCache.inflight[tile.id] = that.loadFromAPI(
41088                         path + tile.extent.toParam(),
41089                         function(err) {
41090                             delete _noteCache.inflight[tile.id];
41091                             if (!err) {
41092                                 _noteCache.loaded[tile.id] = true;
41093                             }
41094                             throttleLoadUsers();
41095                             dispatch$6.call('loadedNotes');
41096                         },
41097                         options
41098                     );
41099                 });
41100             },
41101
41102
41103             // Create a note
41104             // POST /api/0.6/notes?params
41105             postNoteCreate: function(note, callback) {
41106                 if (!this.authenticated()) {
41107                     return callback({ message: 'Not Authenticated', status: -3 }, note);
41108                 }
41109                 if (_noteCache.inflightPost[note.id]) {
41110                     return callback({ message: 'Note update already inflight', status: -2 }, note);
41111                 }
41112
41113                 if (!note.loc[0] || !note.loc[1] || !note.newComment) return; // location & description required
41114
41115                 var comment = note.newComment;
41116                 if (note.newCategory && note.newCategory !== 'None') { comment += ' #' + note.newCategory; }
41117
41118                 var path = '/api/0.6/notes?' + utilQsString({ lon: note.loc[0], lat: note.loc[1], text: comment });
41119
41120                 _noteCache.inflightPost[note.id] = oauth.xhr(
41121                     { method: 'POST', path: path },
41122                     wrapcb(this, done, _connectionID)
41123                 );
41124
41125
41126                 function done(err, xml) {
41127                     delete _noteCache.inflightPost[note.id];
41128                     if (err) { return callback(err); }
41129
41130                     // we get the updated note back, remove from caches and reparse..
41131                     this.removeNote(note);
41132
41133                     var options = { skipSeen: false };
41134                     return parseXML(xml, function(err, results) {
41135                         if (err) {
41136                             return callback(err);
41137                         } else {
41138                             return callback(undefined, results[0]);
41139                         }
41140                     }, options);
41141                 }
41142             },
41143
41144
41145             // Update a note
41146             // POST /api/0.6/notes/#id/comment?text=comment
41147             // POST /api/0.6/notes/#id/close?text=comment
41148             // POST /api/0.6/notes/#id/reopen?text=comment
41149             postNoteUpdate: function(note, newStatus, callback) {
41150                 if (!this.authenticated()) {
41151                     return callback({ message: 'Not Authenticated', status: -3 }, note);
41152                 }
41153                 if (_noteCache.inflightPost[note.id]) {
41154                     return callback({ message: 'Note update already inflight', status: -2 }, note);
41155                 }
41156
41157                 var action;
41158                 if (note.status !== 'closed' && newStatus === 'closed') {
41159                     action = 'close';
41160                 } else if (note.status !== 'open' && newStatus === 'open') {
41161                     action = 'reopen';
41162                 } else {
41163                     action = 'comment';
41164                     if (!note.newComment) return; // when commenting, comment required
41165                 }
41166
41167                 var path = '/api/0.6/notes/' + note.id + '/' + action;
41168                 if (note.newComment) {
41169                     path += '?' + utilQsString({ text: note.newComment });
41170                 }
41171
41172                 _noteCache.inflightPost[note.id] = oauth.xhr(
41173                     { method: 'POST', path: path },
41174                     wrapcb(this, done, _connectionID)
41175                 );
41176
41177
41178                 function done(err, xml) {
41179                     delete _noteCache.inflightPost[note.id];
41180                     if (err) { return callback(err); }
41181
41182                     // we get the updated note back, remove from caches and reparse..
41183                     this.removeNote(note);
41184
41185                     // update closed note cache - used to populate `closed:note` changeset tag
41186                     if (action === 'close') {
41187                         _noteCache.closed[note.id] = true;
41188                     } else if (action === 'reopen') {
41189                         delete _noteCache.closed[note.id];
41190                     }
41191
41192                     var options = { skipSeen: false };
41193                     return parseXML(xml, function(err, results) {
41194                         if (err) {
41195                             return callback(err);
41196                         } else {
41197                             return callback(undefined, results[0]);
41198                         }
41199                     }, options);
41200                 }
41201             },
41202
41203
41204             switch: function(options) {
41205                 urlroot = options.urlroot;
41206
41207                 oauth.options(Object.assign({
41208                     url: urlroot,
41209                     loading: authLoading,
41210                     done: authDone
41211                 }, options));
41212
41213                 this.reset();
41214                 this.userChangesets(function() {});  // eagerly load user details/changesets
41215                 dispatch$6.call('change');
41216                 return this;
41217             },
41218
41219
41220             toggle: function(val) {
41221                 _off = !val;
41222                 return this;
41223             },
41224
41225
41226             isChangesetInflight: function() {
41227                 return !!_changeset.inflight;
41228             },
41229
41230
41231             // get/set cached data
41232             // This is used to save/restore the state when entering/exiting the walkthrough
41233             // Also used for testing purposes.
41234             caches: function(obj) {
41235                 function cloneCache(source) {
41236                     var target = {};
41237                     Object.keys(source).forEach(function(k) {
41238                         if (k === 'rtree') {
41239                             target.rtree = new RBush().fromJSON(source.rtree.toJSON());  // clone rbush
41240                         } else if (k === 'note') {
41241                             target.note = {};
41242                             Object.keys(source.note).forEach(function(id) {
41243                                 target.note[id] = osmNote(source.note[id]);   // copy notes
41244                             });
41245                         } else {
41246                             target[k] = JSON.parse(JSON.stringify(source[k]));   // clone deep
41247                         }
41248                     });
41249                     return target;
41250                 }
41251
41252                 if (!arguments.length) {
41253                     return {
41254                         tile: cloneCache(_tileCache),
41255                         note: cloneCache(_noteCache),
41256                         user: cloneCache(_userCache)
41257                     };
41258                 }
41259
41260                 // access caches directly for testing (e.g., loading notes rtree)
41261                 if (obj === 'get') {
41262                     return {
41263                         tile: _tileCache,
41264                         note: _noteCache,
41265                         user: _userCache
41266                     };
41267                 }
41268
41269                 if (obj.tile) {
41270                     _tileCache = obj.tile;
41271                     _tileCache.inflight = {};
41272                 }
41273                 if (obj.note) {
41274                     _noteCache = obj.note;
41275                     _noteCache.inflight = {};
41276                     _noteCache.inflightPost = {};
41277                 }
41278                 if (obj.user) {
41279                     _userCache = obj.user;
41280                 }
41281
41282                 return this;
41283             },
41284
41285
41286             logout: function() {
41287                 _userChangesets = undefined;
41288                 _userDetails = undefined;
41289                 oauth.logout();
41290                 dispatch$6.call('change');
41291                 return this;
41292             },
41293
41294
41295             authenticated: function() {
41296                 return oauth.authenticated();
41297             },
41298
41299
41300             authenticate: function(callback) {
41301                 var that = this;
41302                 var cid = _connectionID;
41303                 _userChangesets = undefined;
41304                 _userDetails = undefined;
41305
41306                 function done(err, res) {
41307                     if (err) {
41308                         if (callback) callback(err);
41309                         return;
41310                     }
41311                     if (that.getConnectionId() !== cid) {
41312                         if (callback) callback({ message: 'Connection Switched', status: -1 });
41313                         return;
41314                     }
41315                     _rateLimitError = undefined;
41316                     dispatch$6.call('change');
41317                     if (callback) callback(err, res);
41318                     that.userChangesets(function() {});  // eagerly load user details/changesets
41319                 }
41320
41321                 return oauth.authenticate(done);
41322             },
41323
41324
41325             imageryBlacklists: function() {
41326                 return _blacklists;
41327             },
41328
41329
41330             tileZoom: function(val) {
41331                 if (!arguments.length) return _tileZoom$3;
41332                 _tileZoom$3 = val;
41333                 return this;
41334             },
41335
41336
41337             // get all cached notes covering the viewport
41338             notes: function(projection) {
41339                 var viewport = projection.clipExtent();
41340                 var min = [viewport[0][0], viewport[1][1]];
41341                 var max = [viewport[1][0], viewport[0][1]];
41342                 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
41343
41344                 return _noteCache.rtree.search(bbox)
41345                     .map(function(d) { return d.data; });
41346             },
41347
41348
41349             // get a single note from the cache
41350             getNote: function(id) {
41351                 return _noteCache.note[id];
41352             },
41353
41354
41355             // remove a single note from the cache
41356             removeNote: function(note) {
41357                 if (!(note instanceof osmNote) || !note.id) return;
41358
41359                 delete _noteCache.note[note.id];
41360                 updateRtree$3(encodeNoteRtree(note), false);  // false = remove
41361             },
41362
41363
41364             // replace a single note in the cache
41365             replaceNote: function(note) {
41366                 if (!(note instanceof osmNote) || !note.id) return;
41367
41368                 _noteCache.note[note.id] = note;
41369                 updateRtree$3(encodeNoteRtree(note), true);  // true = replace
41370                 return note;
41371             },
41372
41373
41374             // Get an array of note IDs closed during this session.
41375             // Used to populate `closed:note` changeset tag
41376             getClosedIDs: function() {
41377                 return Object.keys(_noteCache.closed).sort();
41378             }
41379
41380         };
41381
41382         var apibase$3 = 'https://wiki.openstreetmap.org/w/api.php';
41383         var _inflight$1 = {};
41384         var _wikibaseCache = {};
41385         var _localeIDs = { en: false };
41386
41387
41388         var debouncedRequest = debounce(request, 500, { leading: false });
41389
41390         function request(url, callback) {
41391             if (_inflight$1[url]) return;
41392             var controller = new AbortController();
41393             _inflight$1[url] = controller;
41394
41395             d3_json(url, { signal: controller.signal })
41396                 .then(function(result) {
41397                     delete _inflight$1[url];
41398                     if (callback) callback(null, result);
41399                 })
41400                 .catch(function(err) {
41401                     delete _inflight$1[url];
41402                     if (err.name === 'AbortError') return;
41403                     if (callback) callback(err.message);
41404                 });
41405         }
41406
41407
41408         /**
41409          * Get the best string value from the descriptions/labels result
41410          * Note that if mediawiki doesn't recognize language code, it will return all values.
41411          * In that case, fallback to use English.
41412          * @param values object - either descriptions or labels
41413          * @param langCode String
41414          * @returns localized string
41415          */
41416         function localizedToString(values, langCode) {
41417             if (values) {
41418                 values = values[langCode] || values.en;
41419             }
41420             return values ? values.value : '';
41421         }
41422
41423
41424         var serviceOsmWikibase = {
41425
41426             init: function() {
41427                 _inflight$1 = {};
41428                 _wikibaseCache = {};
41429                 _localeIDs = {};
41430             },
41431
41432
41433             reset: function() {
41434                 Object.values(_inflight$1).forEach(function(controller) { controller.abort(); });
41435                 _inflight$1 = {};
41436             },
41437
41438
41439             /**
41440              * Get the best value for the property, or undefined if not found
41441              * @param entity object from wikibase
41442              * @param property string e.g. 'P4' for image
41443              * @param langCode string e.g. 'fr' for French
41444              */
41445             claimToValue: function(entity, property, langCode) {
41446                 if (!entity.claims[property]) return undefined;
41447                 var locale = _localeIDs[langCode];
41448                 var preferredPick, localePick;
41449
41450                 entity.claims[property].forEach(function(stmt) {
41451                     // If exists, use value limited to the needed language (has a qualifier P26 = locale)
41452                     // Or if not found, use the first value with the "preferred" rank
41453                     if (!preferredPick && stmt.rank === 'preferred') {
41454                         preferredPick = stmt;
41455                     }
41456                     if (locale && stmt.qualifiers && stmt.qualifiers.P26 &&
41457                         stmt.qualifiers.P26[0].datavalue.value.id === locale
41458                     ) {
41459                         localePick = stmt;
41460                     }
41461                 });
41462
41463                 var result = localePick || preferredPick;
41464                 if (result) {
41465                     var datavalue = result.mainsnak.datavalue;
41466                     return datavalue.type === 'wikibase-entityid' ? datavalue.value.id : datavalue.value;
41467                 } else {
41468                     return undefined;
41469                 }
41470             },
41471
41472
41473             /**
41474              * Convert monolingual property into a key-value object (language -> value)
41475              * @param entity object from wikibase
41476              * @param property string e.g. 'P31' for monolingual wiki page title
41477              */
41478             monolingualClaimToValueObj: function(entity, property) {
41479                 if (!entity || !entity.claims[property]) return undefined;
41480
41481                 return entity.claims[property].reduce(function(acc, obj) {
41482                     var value = obj.mainsnak.datavalue.value;
41483                     acc[value.language] = value.text;
41484                     return acc;
41485                 }, {});
41486             },
41487
41488
41489             toSitelink: function(key, value) {
41490                 var result = value ? ('Tag:' + key + '=' + value) : 'Key:' + key;
41491                 return result.replace(/_/g, ' ').trim();
41492             },
41493
41494
41495             //
41496             // Pass params object of the form:
41497             // {
41498             //   key: 'string',
41499             //   value: 'string',
41500             //   rtype: 'string',
41501             //   langCode: 'string'
41502             // }
41503             //
41504             getEntity: function(params, callback) {
41505                 var doRequest = params.debounce ? debouncedRequest : request;
41506                 var that = this;
41507                 var titles = [];
41508                 var result = {};
41509                 var rtypeSitelink = params.rtype ? ('Relation:' + params.rtype).replace(/_/g, ' ').trim() : false;
41510                 var keySitelink = params.key ? this.toSitelink(params.key) : false;
41511                 var tagSitelink = (params.key && params.value) ? this.toSitelink(params.key, params.value) : false;
41512                 var localeSitelink;
41513
41514                 if (params.langCode && _localeIDs[params.langCode] === undefined) {
41515                     // If this is the first time we are asking about this locale,
41516                     // fetch corresponding entity (if it exists), and cache it.
41517                     // If there is no such entry, cache `false` value to avoid re-requesting it.
41518                     localeSitelink = ('Locale:' + params.langCode).replace(/_/g, ' ').trim();
41519                     titles.push(localeSitelink);
41520                 }
41521
41522                 if (rtypeSitelink) {
41523                     if (_wikibaseCache[rtypeSitelink]) {
41524                         result.rtype = _wikibaseCache[rtypeSitelink];
41525                     } else {
41526                         titles.push(rtypeSitelink);
41527                     }
41528                 }
41529
41530                 if (keySitelink) {
41531                     if (_wikibaseCache[keySitelink]) {
41532                         result.key = _wikibaseCache[keySitelink];
41533                     } else {
41534                         titles.push(keySitelink);
41535                     }
41536                 }
41537
41538                 if (tagSitelink) {
41539                     if (_wikibaseCache[tagSitelink]) {
41540                         result.tag = _wikibaseCache[tagSitelink];
41541                     } else {
41542                         titles.push(tagSitelink);
41543                     }
41544                 }
41545
41546                 if (!titles.length) {
41547                     // Nothing to do, we already had everything in the cache
41548                     return callback(null, result);
41549                 }
41550
41551                 // Requesting just the user language code
41552                 // If backend recognizes the code, it will perform proper fallbacks,
41553                 // and the result will contain the requested code. If not, all values are returned:
41554                 // {"zh-tw":{"value":"...","language":"zh-tw","source-language":"zh-hant"}
41555                 // {"pt-br":{"value":"...","language":"pt","for-language":"pt-br"}}
41556                 var obj = {
41557                     action: 'wbgetentities',
41558                     sites: 'wiki',
41559                     titles: titles.join('|'),
41560                     languages: params.langCode,
41561                     languagefallback: 1,
41562                     origin: '*',
41563                     format: 'json',
41564                     // There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069
41565                     // We shouldn't use v1 until it gets fixed, but should switch to it afterwards
41566                     // formatversion: 2,
41567                 };
41568
41569                 var url = apibase$3 + '?' + utilQsString(obj);
41570                 doRequest(url, function(err, d) {
41571                     if (err) {
41572                         callback(err);
41573                     } else if (!d.success || d.error) {
41574                         callback(d.error.messages.map(function(v) { return v.html['*']; }).join('<br>'));
41575                     } else {
41576                         var localeID = false;
41577                         Object.values(d.entities).forEach(function(res) {
41578                             if (res.missing !== '') {
41579                                 // Simplify access to the localized values
41580                                 res.description = localizedToString(res.descriptions, params.langCode);
41581                                 res.label = localizedToString(res.labels, params.langCode);
41582
41583                                 var title = res.sitelinks.wiki.title;
41584                                 if (title === rtypeSitelink) {
41585                                     _wikibaseCache[rtypeSitelink] = res;
41586                                     result.rtype = res;
41587                                 } else if (title === keySitelink) {
41588                                     _wikibaseCache[keySitelink] = res;
41589                                     result.key = res;
41590                                 } else if (title === tagSitelink) {
41591                                     _wikibaseCache[tagSitelink] = res;
41592                                     result.tag = res;
41593                                 } else if (title === localeSitelink) {
41594                                     localeID = res.id;
41595                                 } else {
41596                                     console.log('Unexpected title ' + title);  // eslint-disable-line no-console
41597                                 }
41598                             }
41599                         });
41600
41601                         if (localeSitelink) {
41602                             // If locale ID is not found, store false to prevent repeated queries
41603                             that.addLocale(params.langCode, localeID);
41604                         }
41605
41606                         callback(null, result);
41607                     }
41608                 });
41609             },
41610
41611
41612             //
41613             // Pass params object of the form:
41614             // {
41615             //   key: 'string',     // required
41616             //   value: 'string'    // optional
41617             // }
41618             //   -or-
41619             // {
41620             //   rtype: 'rtype'     // relation type  (e.g. 'multipolygon')
41621             // }
41622             //
41623             // Get an result object used to display tag documentation
41624             // {
41625             //   title:        'string',
41626             //   description:  'string',
41627             //   editURL:      'string',
41628             //   imageURL:     'string',
41629             //   wiki:         { title: 'string', text: 'string', url: 'string' }
41630             // }
41631             //
41632             getDocs: function(params, callback) {
41633                 var that = this;
41634                 var langCode = _mainLocalizer.localeCode().toLowerCase();
41635                 params.langCode = langCode;
41636
41637                 this.getEntity(params, function(err, data) {
41638                     if (err) {
41639                         callback(err);
41640                         return;
41641                     }
41642
41643                     var entity = data.rtype || data.tag || data.key;
41644                     if (!entity) {
41645                         callback('No entity');
41646                         return;
41647                     }
41648
41649                     // prepare result
41650                     var result = {
41651                         title: entity.title,
41652                         description: entity.description,
41653                         editURL: 'https://wiki.openstreetmap.org/wiki/' + entity.title
41654                     };
41655
41656                     // add image
41657                     if (entity.claims) {
41658                         var imageroot;
41659                         var image = that.claimToValue(entity, 'P4', langCode);
41660                         if (image) {
41661                             imageroot = 'https://commons.wikimedia.org/w/index.php';
41662                         } else {
41663                             image = that.claimToValue(entity, 'P28', langCode);
41664                             if (image) {
41665                                 imageroot = 'https://wiki.openstreetmap.org/w/index.php';
41666                             }
41667                         }
41668                         if (imageroot && image) {
41669                             result.imageURL = imageroot + '?' + utilQsString({
41670                                 title: 'Special:Redirect/file/' + image,
41671                                 width: 400
41672                             });
41673                         }
41674                     }
41675
41676                     // Try to get a wiki page from tag data item first, followed by the corresponding key data item.
41677                     // If neither tag nor key data item contain a wiki page in the needed language nor English,
41678                     // get the first found wiki page from either the tag or the key item.
41679                     var rtypeWiki = that.monolingualClaimToValueObj(data.rtype, 'P31');
41680                     var tagWiki = that.monolingualClaimToValueObj(data.tag, 'P31');
41681                     var keyWiki = that.monolingualClaimToValueObj(data.key, 'P31');
41682
41683                     // If exact language code does not exist, try to find the first part before the '-'
41684                     // BUG: in some cases, a more elaborate fallback logic might be needed
41685                     var langPrefix = langCode.split('-', 2)[0];
41686
41687                     // use the first acceptable wiki page
41688                     result.wiki =
41689                         getWikiInfo(rtypeWiki, langCode, 'inspector.wiki_reference') ||
41690                         getWikiInfo(rtypeWiki, langPrefix, 'inspector.wiki_reference') ||
41691                         getWikiInfo(rtypeWiki, 'en', 'inspector.wiki_en_reference') ||
41692                         getWikiInfo(tagWiki, langCode, 'inspector.wiki_reference') ||
41693                         getWikiInfo(tagWiki, langPrefix, 'inspector.wiki_reference') ||
41694                         getWikiInfo(tagWiki, 'en', 'inspector.wiki_en_reference') ||
41695                         getWikiInfo(keyWiki, langCode, 'inspector.wiki_reference') ||
41696                         getWikiInfo(keyWiki, langPrefix, 'inspector.wiki_reference') ||
41697                         getWikiInfo(keyWiki, 'en', 'inspector.wiki_en_reference');
41698
41699                     callback(null, result);
41700
41701
41702                     // Helper method to get wiki info if a given language exists
41703                     function getWikiInfo(wiki, langCode, tKey) {
41704                         if (wiki && wiki[langCode]) {
41705                             return {
41706                                 title: wiki[langCode],
41707                                 text: tKey,
41708                                 url: 'https://wiki.openstreetmap.org/wiki/' + wiki[langCode]
41709                             };
41710                         }
41711                     }
41712                 });
41713             },
41714
41715
41716             addLocale: function(langCode, qid) {
41717                 // Makes it easier to unit test
41718                 _localeIDs[langCode] = qid;
41719             },
41720
41721
41722             apibase: function(val) {
41723                 if (!arguments.length) return apibase$3;
41724                 apibase$3 = val;
41725                 return this;
41726             }
41727
41728         };
41729
41730         var jsonpCache = {};
41731         window.jsonpCache = jsonpCache;
41732
41733         function jsonpRequest(url, callback) {
41734             var request = {
41735                 abort: function() {}
41736             };
41737
41738             if (window.JSONP_FIX) {
41739                 if (window.JSONP_DELAY === 0) {
41740                     callback(window.JSONP_FIX);
41741                 } else {
41742                     var t = window.setTimeout(function() {
41743                         callback(window.JSONP_FIX);
41744                     }, window.JSONP_DELAY || 0);
41745
41746                     request.abort = function() { window.clearTimeout(t); };
41747                 }
41748
41749                 return request;
41750             }
41751
41752             function rand() {
41753                 var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
41754                 var c = '';
41755                 var i = -1;
41756                 while (++i < 15) c += chars.charAt(Math.floor(Math.random() * 52));
41757                 return c;
41758             }
41759
41760             function create(url) {
41761                 var e = url.match(/callback=(\w+)/);
41762                 var c = e ? e[1] : rand();
41763
41764                 jsonpCache[c] = function(data) {
41765                     if (jsonpCache[c]) {
41766                         callback(data);
41767                     }
41768                     finalize();
41769                 };
41770
41771                 function finalize() {
41772                     delete jsonpCache[c];
41773                     script.remove();
41774                 }
41775
41776                 request.abort = finalize;
41777                 return 'jsonpCache.' + c;
41778             }
41779
41780             var cb = create(url);
41781
41782             var script = select('head')
41783                 .append('script')
41784                 .attr('type', 'text/javascript')
41785                 .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
41786
41787             return request;
41788         }
41789
41790         const bubbleApi = 'https://dev.virtualearth.net/mapcontrol/HumanScaleServices/GetBubbles.ashx?';
41791         const streetsideImagesApi = 'https://t.ssl.ak.tiles.virtualearth.net/tiles/';
41792         const bubbleAppKey = 'AuftgJsO0Xs8Ts4M1xZUQJQXJNsvmh3IV8DkNieCiy3tCwCUMq76-WpkrBtNAuEm';
41793         const pannellumViewerCSS = 'pannellum-streetside/pannellum.css';
41794         const pannellumViewerJS = 'pannellum-streetside/pannellum.js';
41795         const maxResults$2 = 2000;
41796         const tileZoom$2 = 16.5;
41797         const tiler$6 = utilTiler().zoomExtent([tileZoom$2, tileZoom$2]).skipNullIsland(true);
41798         const dispatch$7 = dispatch('loadedBubbles', 'viewerChanged');
41799         const minHfov = 10;         // zoom in degrees:  20, 10, 5
41800         const maxHfov = 90;         // zoom out degrees
41801         const defaultHfov = 45;
41802
41803         let _hires = false;
41804         let _resolution = 512;    // higher numbers are slower - 512, 1024, 2048, 4096
41805         let _currScene = 0;
41806         let _ssCache;
41807         let _pannellumViewer;
41808         let _sceneOptions;
41809         let _dataUrlArray = [];
41810
41811
41812         /**
41813          * abortRequest().
41814          */
41815         function abortRequest$6(i) {
41816           i.abort();
41817         }
41818
41819
41820         /**
41821          * localeTimeStamp().
41822          */
41823         function localeTimestamp(s) {
41824           if (!s) return null;
41825           const options = { day: 'numeric', month: 'short', year: 'numeric' };
41826           const d = new Date(s);
41827           if (isNaN(d.getTime())) return null;
41828           return d.toLocaleString(_mainLocalizer.localeCode(), options);
41829         }
41830
41831
41832         /**
41833          * loadTiles() wraps the process of generating tiles and then fetching image points for each tile.
41834          */
41835         function loadTiles$2(which, url, projection, margin) {
41836           const tiles = tiler$6.margin(margin).getTiles(projection);
41837
41838           // abort inflight requests that are no longer needed
41839           const cache = _ssCache[which];
41840           Object.keys(cache.inflight).forEach(k => {
41841             const wanted = tiles.find(tile => k.indexOf(tile.id + ',') === 0);
41842             if (!wanted) {
41843               abortRequest$6(cache.inflight[k]);
41844               delete cache.inflight[k];
41845             }
41846           });
41847
41848           tiles.forEach(tile => loadNextTilePage$2(which, url, tile));
41849         }
41850
41851
41852         /**
41853          * loadNextTilePage() load data for the next tile page in line.
41854          */
41855         function loadNextTilePage$2(which, url, tile) {
41856           const cache = _ssCache[which];
41857           const nextPage = cache.nextPage[tile.id] || 0;
41858           const id = tile.id + ',' + String(nextPage);
41859           if (cache.loaded[id] || cache.inflight[id]) return;
41860
41861           cache.inflight[id] = getBubbles(url, tile, (bubbles) => {
41862             cache.loaded[id] = true;
41863             delete cache.inflight[id];
41864             if (!bubbles) return;
41865
41866             // [].shift() removes the first element, some statistics info, not a bubble point
41867             bubbles.shift();
41868
41869             const features = bubbles.map(bubble => {
41870               if (cache.points[bubble.id]) return null;  // skip duplicates
41871
41872               const loc = [bubble.lo, bubble.la];
41873               const d = {
41874                 loc: loc,
41875                 key: bubble.id,
41876                 ca: bubble.he,
41877                 captured_at: bubble.cd,
41878                 captured_by: 'microsoft',
41879                 // nbn: bubble.nbn,
41880                 // pbn: bubble.pbn,
41881                 // ad: bubble.ad,
41882                 // rn: bubble.rn,
41883                 pr: bubble.pr,  // previous
41884                 ne: bubble.ne,  // next
41885                 pano: true,
41886                 sequenceKey: null
41887               };
41888
41889               cache.points[bubble.id] = d;
41890
41891               // a sequence starts here
41892               if (bubble.pr === undefined) {
41893                 cache.leaders.push(bubble.id);
41894               }
41895
41896               return {
41897                 minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
41898               };
41899
41900             }).filter(Boolean);
41901
41902             cache.rtree.load(features);
41903
41904             connectSequences();
41905
41906             if (which === 'bubbles') {
41907               dispatch$7.call('loadedBubbles');
41908             }
41909           });
41910         }
41911
41912
41913         // call this sometimes to connect the bubbles into sequences
41914         function connectSequences() {
41915           let cache = _ssCache.bubbles;
41916           let keepLeaders = [];
41917
41918           for (let i = 0; i < cache.leaders.length; i++) {
41919             let bubble = cache.points[cache.leaders[i]];
41920             let seen = {};
41921
41922             // try to make a sequence.. use the key of the leader bubble.
41923             let sequence = { key: bubble.key, bubbles: [] };
41924             let complete = false;
41925
41926             do {
41927               sequence.bubbles.push(bubble);
41928               seen[bubble.key] = true;
41929
41930               if (bubble.ne === undefined) {
41931                 complete = true;
41932               } else {
41933                 bubble = cache.points[bubble.ne];  // advance to next
41934               }
41935             } while (bubble && !seen[bubble.key] && !complete);
41936
41937
41938             if (complete) {
41939               _ssCache.sequences[sequence.key] = sequence;
41940
41941               // assign bubbles to the sequence
41942               for (let j = 0; j < sequence.bubbles.length; j++) {
41943                 sequence.bubbles[j].sequenceKey = sequence.key;
41944               }
41945
41946               // create a GeoJSON LineString
41947               sequence.geojson = {
41948                 type: 'LineString',
41949                 properties: { key: sequence.key },
41950                 coordinates: sequence.bubbles.map(d => d.loc)
41951               };
41952
41953             } else {
41954               keepLeaders.push(cache.leaders[i]);
41955             }
41956           }
41957
41958           // couldn't complete these, save for later
41959           cache.leaders = keepLeaders;
41960         }
41961
41962
41963         /**
41964          * getBubbles() handles the request to the server for a tile extent of 'bubbles' (streetside image locations).
41965          */
41966         function getBubbles(url, tile, callback) {
41967           let rect = tile.extent.rectangle();
41968           let urlForRequest = url + utilQsString({
41969             n: rect[3],
41970             s: rect[1],
41971             e: rect[2],
41972             w: rect[0],
41973             c: maxResults$2,
41974             appkey: bubbleAppKey,
41975             jsCallback: '{callback}'
41976           });
41977
41978           return jsonpRequest(urlForRequest, (data) => {
41979             if (!data || data.error) {
41980               callback(null);
41981             } else {
41982               callback(data);
41983             }
41984           });
41985         }
41986
41987
41988         // partition viewport into higher zoom tiles
41989         function partitionViewport$2(projection) {
41990           let z = geoScaleToZoom(projection.scale());
41991           let z2 = (Math.ceil(z * 2) / 2) + 2.5;   // round to next 0.5 and add 2.5
41992           let tiler = utilTiler().zoomExtent([z2, z2]);
41993
41994           return tiler.getTiles(projection)
41995             .map(tile => tile.extent);
41996         }
41997
41998
41999         // no more than `limit` results per partition.
42000         function searchLimited$2(limit, projection, rtree) {
42001           limit = limit || 5;
42002
42003           return partitionViewport$2(projection)
42004             .reduce((result, extent) => {
42005               let found = rtree.search(extent.bbox())
42006                 .slice(0, limit)
42007                 .map(d => d.data);
42008
42009               return (found.length ? result.concat(found) : result);
42010             }, []);
42011         }
42012
42013
42014         /**
42015          * loadImage()
42016          */
42017         function loadImage(imgInfo) {
42018           return new Promise(resolve => {
42019             let img = new Image();
42020             img.onload = () => {
42021               let canvas = document.getElementById('ideditor-canvas' + imgInfo.face);
42022               let ctx = canvas.getContext('2d');
42023               ctx.drawImage(img, imgInfo.x, imgInfo.y);
42024               resolve({ imgInfo: imgInfo, status: 'ok' });
42025             };
42026             img.onerror = () => {
42027               resolve({ data: imgInfo, status: 'error' });
42028             };
42029             img.setAttribute('crossorigin', '');
42030             img.src = imgInfo.url;
42031           });
42032         }
42033
42034
42035         /**
42036          * loadCanvas()
42037          */
42038         function loadCanvas(imageGroup) {
42039           return Promise.all(imageGroup.map(loadImage))
42040             .then((data) => {
42041               let canvas = document.getElementById('ideditor-canvas' + data[0].imgInfo.face);
42042               const which = { '01': 0, '02': 1, '03': 2, '10': 3, '11': 4, '12': 5 };
42043               let face = data[0].imgInfo.face;
42044               _dataUrlArray[which[face]] = canvas.toDataURL('image/jpeg', 1.0);
42045               return { status: 'loadCanvas for face ' + data[0].imgInfo.face + 'ok'};
42046             });
42047         }
42048
42049
42050         /**
42051          * loadFaces()
42052          */
42053         function loadFaces(faceGroup) {
42054           return Promise.all(faceGroup.map(loadCanvas))
42055             .then(() => { return { status: 'loadFaces done' }; });
42056         }
42057
42058
42059         function setupCanvas(selection, reset) {
42060           if (reset) {
42061             selection.selectAll('#ideditor-stitcher-canvases')
42062               .remove();
42063           }
42064
42065           // Add the Streetside working canvases. These are used for 'stitching', or combining,
42066           // multiple images for each of the six faces, before passing to the Pannellum control as DataUrls
42067           selection.selectAll('#ideditor-stitcher-canvases')
42068             .data([0])
42069             .enter()
42070             .append('div')
42071             .attr('id', 'ideditor-stitcher-canvases')
42072             .attr('display', 'none')
42073             .selectAll('canvas')
42074             .data(['canvas01', 'canvas02', 'canvas03', 'canvas10', 'canvas11', 'canvas12'])
42075             .enter()
42076             .append('canvas')
42077             .attr('id', d => 'ideditor-' + d)
42078             .attr('width', _resolution)
42079             .attr('height', _resolution);
42080         }
42081
42082
42083         function qkToXY(qk) {
42084           let x = 0;
42085           let y = 0;
42086           let scale = 256;
42087           for (let i = qk.length; i > 0; i--) {
42088             const key = qk[i-1];
42089             x += (+(key === '1' || key === '3')) * scale;
42090             y += (+(key === '2' || key === '3')) * scale;
42091             scale *= 2;
42092           }
42093           return [x, y];
42094         }
42095
42096
42097         function getQuadKeys() {
42098           let dim = _resolution / 256;
42099           let quadKeys;
42100
42101           if (dim === 16) {
42102             quadKeys = [
42103               '0000','0001','0010','0011','0100','0101','0110','0111',  '1000','1001','1010','1011','1100','1101','1110','1111',
42104               '0002','0003','0012','0013','0102','0103','0112','0113',  '1002','1003','1012','1013','1102','1103','1112','1113',
42105               '0020','0021','0030','0031','0120','0121','0130','0131',  '1020','1021','1030','1031','1120','1121','1130','1131',
42106               '0022','0023','0032','0033','0122','0123','0132','0133',  '1022','1023','1032','1033','1122','1123','1132','1133',
42107               '0200','0201','0210','0211','0300','0301','0310','0311',  '1200','1201','1210','1211','1300','1301','1310','1311',
42108               '0202','0203','0212','0213','0302','0303','0312','0313',  '1202','1203','1212','1213','1302','1303','1312','1313',
42109               '0220','0221','0230','0231','0320','0321','0330','0331',  '1220','1221','1230','1231','1320','1321','1330','1331',
42110               '0222','0223','0232','0233','0322','0323','0332','0333',  '1222','1223','1232','1233','1322','1323','1332','1333',
42111
42112               '2000','2001','2010','2011','2100','2101','2110','2111',  '3000','3001','3010','3011','3100','3101','3110','3111',
42113               '2002','2003','2012','2013','2102','2103','2112','2113',  '3002','3003','3012','3013','3102','3103','3112','3113',
42114               '2020','2021','2030','2031','2120','2121','2130','2131',  '3020','3021','3030','3031','3120','3121','3130','3131',
42115               '2022','2023','2032','2033','2122','2123','2132','2133',  '3022','3023','3032','3033','3122','3123','3132','3133',
42116               '2200','2201','2210','2211','2300','2301','2310','2311',  '3200','3201','3210','3211','3300','3301','3310','3311',
42117               '2202','2203','2212','2213','2302','2303','2312','2313',  '3202','3203','3212','3213','3302','3303','3312','3313',
42118               '2220','2221','2230','2231','2320','2321','2330','2331',  '3220','3221','3230','3231','3320','3321','3330','3331',
42119               '2222','2223','2232','2233','2322','2323','2332','2333',  '3222','3223','3232','3233','3322','3323','3332','3333'
42120             ];
42121
42122           } else if (dim === 8) {
42123             quadKeys = [
42124               '000','001','010','011',  '100','101','110','111',
42125               '002','003','012','013',  '102','103','112','113',
42126               '020','021','030','031',  '120','121','130','131',
42127               '022','023','032','033',  '122','123','132','133',
42128
42129               '200','201','210','211',  '300','301','310','311',
42130               '202','203','212','213',  '302','303','312','313',
42131               '220','221','230','231',  '320','321','330','331',
42132               '222','223','232','233',  '322','323','332','333'
42133             ];
42134
42135           } else if (dim === 4) {
42136             quadKeys = [
42137               '00','01',  '10','11',
42138               '02','03',  '12','13',
42139
42140               '20','21',  '30','31',
42141               '22','23',  '32','33'
42142             ];
42143
42144           } else {  // dim === 2
42145             quadKeys = [
42146               '0', '1',
42147               '2', '3'
42148             ];
42149           }
42150
42151           return quadKeys;
42152         }
42153
42154
42155
42156         var serviceStreetside = {
42157           /**
42158            * init() initialize streetside.
42159            */
42160           init: function() {
42161             if (!_ssCache) {
42162               this.reset();
42163             }
42164
42165             this.event = utilRebind(this, dispatch$7, 'on');
42166           },
42167
42168           /**
42169            * reset() reset the cache.
42170            */
42171           reset: function() {
42172             if (_ssCache) {
42173               Object.values(_ssCache.bubbles.inflight).forEach(abortRequest$6);
42174             }
42175
42176             _ssCache = {
42177               bubbles: { inflight: {}, loaded: {}, nextPage: {}, rtree: new RBush(), points: {}, leaders: [] },
42178               sequences: {}
42179             };
42180           },
42181
42182           /**
42183            * bubbles()
42184            */
42185           bubbles: function(projection) {
42186             const limit = 5;
42187             return searchLimited$2(limit, projection, _ssCache.bubbles.rtree);
42188           },
42189
42190
42191           sequences: function(projection) {
42192             const viewport = projection.clipExtent();
42193             const min = [viewport[0][0], viewport[1][1]];
42194             const max = [viewport[1][0], viewport[0][1]];
42195             const bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
42196             let seen = {};
42197             let results = [];
42198
42199             // all sequences for bubbles in viewport
42200             _ssCache.bubbles.rtree.search(bbox)
42201               .forEach(d => {
42202                 const key = d.data.sequenceKey;
42203                 if (key && !seen[key]) {
42204                     seen[key] = true;
42205                     results.push(_ssCache.sequences[key].geojson);
42206                 }
42207               });
42208
42209             return results;
42210           },
42211
42212
42213           /**
42214            * loadBubbles()
42215            */
42216           loadBubbles: function(projection, margin) {
42217             // by default: request 2 nearby tiles so we can connect sequences.
42218             if (margin === undefined) margin = 2;
42219
42220             loadTiles$2('bubbles', bubbleApi, projection, margin);
42221           },
42222
42223
42224           viewer: function() {
42225             return _pannellumViewer;
42226           },
42227
42228
42229           initViewer: function () {
42230             if (!window.pannellum) return;
42231             if (_pannellumViewer) return;
42232
42233             const sceneID = ++_currScene + '';
42234             const options = {
42235               'default': { firstScene: sceneID },
42236               scenes: {}
42237             };
42238             options.scenes[sceneID] = _sceneOptions;
42239
42240             _pannellumViewer = window.pannellum.viewer('ideditor-viewer-streetside', options);
42241           },
42242
42243
42244           /**
42245            * loadViewer() create the streeside viewer.
42246            */
42247           loadViewer: function(context) {
42248             let that = this;
42249
42250             let pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
42251
42252             // create ms-wrapper, a photo wrapper class
42253             let wrap = context.container().select('.photoviewer').selectAll('.ms-wrapper')
42254               .data([0]);
42255
42256             // inject ms-wrapper into the photoviewer div
42257             // (used by all to house each custom photo viewer)
42258             let wrapEnter = wrap.enter()
42259               .append('div')
42260               .attr('class', 'photo-wrapper ms-wrapper')
42261               .classed('hide', true);
42262
42263             // inject div to support streetside viewer (pannellum) and attribution line
42264             wrapEnter
42265               .append('div')
42266               .attr('id', 'ideditor-viewer-streetside')
42267               .on(pointerPrefix + 'down.streetside', () => {
42268                 select(window)
42269                   .on(pointerPrefix + 'move.streetside', () => {
42270                     dispatch$7.call('viewerChanged');
42271                   }, true);
42272               })
42273               .on(pointerPrefix + 'up.streetside pointercancel.streetside', () => {
42274                 select(window)
42275                   .on(pointerPrefix + 'move.streetside', null);
42276
42277                 // continue dispatching events for a few seconds, in case viewer has inertia.
42278                 let t = timer(elapsed => {
42279                   dispatch$7.call('viewerChanged');
42280                   if (elapsed > 2000) {
42281                     t.stop();
42282                   }
42283                 });
42284               })
42285               .append('div')
42286               .attr('class', 'photo-attribution fillD');
42287
42288             let controlsEnter = wrapEnter
42289               .append('div')
42290               .attr('class', 'photo-controls-wrap')
42291               .append('div')
42292               .attr('class', 'photo-controls');
42293
42294             controlsEnter
42295               .append('button')
42296               .on('click.back', step(-1))
42297               .text('◄');
42298
42299             controlsEnter
42300               .append('button')
42301               .on('click.forward', step(1))
42302               .text('►');
42303
42304
42305             // create working canvas for stitching together images
42306             wrap = wrap
42307               .merge(wrapEnter)
42308               .call(setupCanvas, true);
42309
42310             // load streetside pannellum viewer css
42311             select('head').selectAll('#ideditor-streetside-viewercss')
42312               .data([0])
42313               .enter()
42314               .append('link')
42315               .attr('id', 'ideditor-streetside-viewercss')
42316               .attr('rel', 'stylesheet')
42317               .attr('href', context.asset(pannellumViewerCSS));
42318
42319             // load streetside pannellum viewer js
42320             select('head').selectAll('#ideditor-streetside-viewerjs')
42321               .data([0])
42322               .enter()
42323               .append('script')
42324               .attr('id', 'ideditor-streetside-viewerjs')
42325               .attr('src', context.asset(pannellumViewerJS));
42326
42327
42328             // Register viewer resize handler
42329             context.ui().photoviewer.on('resize.streetside', () => {
42330               if (_pannellumViewer) {
42331                 _pannellumViewer.resize();
42332               }
42333             });
42334
42335
42336             function step(stepBy) {
42337               return () => {
42338                 let viewer = context.container().select('.photoviewer');
42339                 let selected = viewer.empty() ? undefined : viewer.datum();
42340                 if (!selected) return;
42341
42342                 let nextID = (stepBy === 1 ? selected.ne : selected.pr);
42343                 let yaw = _pannellumViewer.getYaw();
42344                 let ca = selected.ca + yaw;
42345                 let origin = selected.loc;
42346
42347                 // construct a search trapezoid pointing out from current bubble
42348                 const meters = 35;
42349                 let p1 = [
42350                   origin[0] + geoMetersToLon(meters / 5, origin[1]),
42351                   origin[1]
42352                 ];
42353                 let p2 = [
42354                   origin[0] + geoMetersToLon(meters / 2, origin[1]),
42355                   origin[1] + geoMetersToLat(meters)
42356                 ];
42357                 let p3 = [
42358                   origin[0] - geoMetersToLon(meters / 2, origin[1]),
42359                   origin[1] + geoMetersToLat(meters)
42360                 ];
42361                 let p4 = [
42362                   origin[0] - geoMetersToLon(meters / 5, origin[1]),
42363                   origin[1]
42364                 ];
42365
42366                 let poly = [p1, p2, p3, p4, p1];
42367
42368                 // rotate it to face forward/backward
42369                 let angle = (stepBy === 1 ? ca : ca + 180) * (Math.PI / 180);
42370                 poly = geoRotate(poly, -angle, origin);
42371
42372                 let extent = poly.reduce((extent, point) => {
42373                   return extent.extend(geoExtent(point));
42374                 }, geoExtent());
42375
42376                 // find nearest other bubble in the search polygon
42377                 let minDist = Infinity;
42378                 _ssCache.bubbles.rtree.search(extent.bbox())
42379                   .forEach(d => {
42380                     if (d.data.key === selected.key) return;
42381                     if (!geoPointInPolygon(d.data.loc, poly)) return;
42382
42383                     let dist = geoVecLength(d.data.loc, selected.loc);
42384                     let theta = selected.ca - d.data.ca;
42385                     let minTheta = Math.min(Math.abs(theta), 360 - Math.abs(theta));
42386                     if (minTheta > 20) {
42387                       dist += 5;  // penalize distance if camera angles don't match
42388                     }
42389
42390                     if (dist < minDist) {
42391                       nextID = d.data.key;
42392                       minDist = dist;
42393                     }
42394                   });
42395
42396                 let nextBubble = nextID && _ssCache.bubbles.points[nextID];
42397                 if (!nextBubble) return;
42398
42399                 context.map().centerEase(nextBubble.loc);
42400
42401                 that.selectImage(context, nextBubble)
42402                   .then(response => {
42403                     if (response.status === 'ok') {
42404                       _sceneOptions.yaw = yaw;
42405                       that.showViewer(context);
42406                     }
42407                   });
42408               };
42409             }
42410           },
42411
42412
42413           /**
42414            * showViewer()
42415            */
42416           showViewer: function(context, yaw) {
42417             if (!_sceneOptions) return;
42418
42419             if (yaw !== undefined) {
42420               _sceneOptions.yaw = yaw;
42421             }
42422
42423             if (!_pannellumViewer) {
42424               this.initViewer();
42425             } else {
42426               // make a new scene
42427               let sceneID = ++_currScene + '';
42428               _pannellumViewer
42429                 .addScene(sceneID, _sceneOptions)
42430                 .loadScene(sceneID);
42431
42432               // remove previous scene
42433               if (_currScene > 2) {
42434                 sceneID = (_currScene - 1) + '';
42435                 _pannellumViewer
42436                   .removeScene(sceneID);
42437               }
42438             }
42439
42440             let wrap = context.container().select('.photoviewer')
42441               .classed('hide', false);
42442
42443             let isHidden = wrap.selectAll('.photo-wrapper.ms-wrapper.hide').size();
42444
42445             if (isHidden) {
42446               wrap
42447                 .selectAll('.photo-wrapper:not(.ms-wrapper)')
42448                 .classed('hide', true);
42449
42450               wrap
42451                 .selectAll('.photo-wrapper.ms-wrapper')
42452                 .classed('hide', false);
42453             }
42454
42455             return this;
42456           },
42457
42458
42459           /**
42460            * hideViewer()
42461            */
42462           hideViewer: function (context) {
42463             let viewer = context.container().select('.photoviewer');
42464             if (!viewer.empty()) viewer.datum(null);
42465
42466             viewer
42467               .classed('hide', true)
42468               .selectAll('.photo-wrapper')
42469               .classed('hide', true);
42470
42471             context.container().selectAll('.viewfield-group, .sequence, .icon-sign')
42472               .classed('currentView', false);
42473
42474             return this.setStyles(context, null, true);
42475           },
42476
42477
42478           /**
42479            * selectImage().
42480            */
42481           selectImage: function (context, d) {
42482             let that = this;
42483             let viewer = context.container().select('.photoviewer');
42484             if (!viewer.empty()) viewer.datum(d);
42485
42486             this.setStyles(context, null, true);
42487
42488             let wrap = context.container().select('.photoviewer .ms-wrapper');
42489             let attribution = wrap.selectAll('.photo-attribution').html('');
42490
42491             wrap.selectAll('.pnlm-load-box')   // display "loading.."
42492               .style('display', 'block');
42493
42494             if (!d) {
42495               return Promise.resolve({ status: 'ok' });
42496             }
42497
42498             let line1 = attribution
42499               .append('div')
42500               .attr('class', 'attribution-row');
42501
42502             const hiresDomId = utilUniqueDomId('streetside-hires');
42503
42504             // Add hires checkbox
42505             let label = line1
42506               .append('label')
42507               .attr('for', hiresDomId)
42508               .attr('class', 'streetside-hires');
42509
42510             label
42511               .append('input')
42512               .attr('type', 'checkbox')
42513               .attr('id', hiresDomId)
42514               .property('checked', _hires)
42515               .on('click', () => {
42516                 event.stopPropagation();
42517
42518                 _hires = !_hires;
42519                 _resolution = _hires ? 1024 : 512;
42520                 wrap.call(setupCanvas, true);
42521
42522                 let viewstate = {
42523                   yaw: _pannellumViewer.getYaw(),
42524                   pitch: _pannellumViewer.getPitch(),
42525                   hfov: _pannellumViewer.getHfov()
42526                 };
42527
42528                 that.selectImage(context, d)
42529                   .then(response => {
42530                     if (response.status === 'ok') {
42531                       _sceneOptions = Object.assign(_sceneOptions, viewstate);
42532                       that.showViewer(context);
42533                     }
42534                   });
42535               });
42536
42537             label
42538               .append('span')
42539               .text(_t('streetside.hires'));
42540
42541
42542             let captureInfo = line1
42543               .append('div')
42544               .attr('class', 'attribution-capture-info');
42545
42546             // Add capture date
42547             if (d.captured_by) {
42548               const yyyy = (new Date()).getFullYear();
42549
42550               captureInfo
42551                 .append('a')
42552                 .attr('class', 'captured_by')
42553                 .attr('target', '_blank')
42554                 .attr('href', 'https://www.microsoft.com/en-us/maps/streetside')
42555                 .text('©' + yyyy + ' Microsoft');
42556
42557               captureInfo
42558                 .append('span')
42559                 .text('|');
42560             }
42561
42562             if (d.captured_at) {
42563               captureInfo
42564                 .append('span')
42565                 .attr('class', 'captured_at')
42566                 .text(localeTimestamp(d.captured_at));
42567             }
42568
42569             // Add image links
42570             let line2 = attribution
42571               .append('div')
42572               .attr('class', 'attribution-row');
42573
42574             line2
42575               .append('a')
42576               .attr('class', 'image-view-link')
42577               .attr('target', '_blank')
42578               .attr('href', 'https://www.bing.com/maps?cp=' + d.loc[1] + '~' + d.loc[0] +
42579                 '&lvl=17&dir=' + d.ca + '&style=x&v=2&sV=1')
42580               .text(_t('streetside.view_on_bing'));
42581
42582             line2
42583               .append('a')
42584               .attr('class', 'image-report-link')
42585               .attr('target', '_blank')
42586               .attr('href', 'https://www.bing.com/maps/privacyreport/streetsideprivacyreport?bubbleid=' +
42587                 encodeURIComponent(d.key) + '&focus=photo&lat=' + d.loc[1] + '&lng=' + d.loc[0] + '&z=17')
42588               .text(_t('streetside.report'));
42589
42590
42591             let bubbleIdQuadKey = d.key.toString(4);
42592             const paddingNeeded = 16 - bubbleIdQuadKey.length;
42593             for (let i = 0; i < paddingNeeded; i++) {
42594               bubbleIdQuadKey = '0' + bubbleIdQuadKey;
42595             }
42596             const imgUrlPrefix = streetsideImagesApi + 'hs' + bubbleIdQuadKey;
42597             const imgUrlSuffix = '.jpg?g=6338&n=z';
42598
42599             // Cubemap face code order matters here: front=01, right=02, back=03, left=10, up=11, down=12
42600             const faceKeys = ['01','02','03','10','11','12'];
42601
42602             // Map images to cube faces
42603             let quadKeys = getQuadKeys();
42604             let faces = faceKeys.map((faceKey) => {
42605               return quadKeys.map((quadKey) =>{
42606                 const xy = qkToXY(quadKey);
42607                 return {
42608                   face: faceKey,
42609                   url: imgUrlPrefix + faceKey + quadKey + imgUrlSuffix,
42610                   x: xy[0],
42611                   y: xy[1]
42612                 };
42613               });
42614             });
42615
42616             return loadFaces(faces)
42617               .then(() => {
42618                 _sceneOptions = {
42619                   showFullscreenCtrl: false,
42620                   autoLoad: true,
42621                   compass: true,
42622                   northOffset: d.ca,
42623                   yaw: 0,
42624                   minHfov: minHfov,
42625                   maxHfov: maxHfov,
42626                   hfov: defaultHfov,
42627                   type: 'cubemap',
42628                   cubeMap: [
42629                     _dataUrlArray[0],
42630                     _dataUrlArray[1],
42631                     _dataUrlArray[2],
42632                     _dataUrlArray[3],
42633                     _dataUrlArray[4],
42634                     _dataUrlArray[5]
42635                   ]
42636                 };
42637                 return { status: 'ok' };
42638               });
42639           },
42640
42641
42642           getSequenceKeyForBubble: function(d) {
42643             return d && d.sequenceKey;
42644           },
42645
42646
42647           // Updates the currently highlighted sequence and selected bubble.
42648           // Reset is only necessary when interacting with the viewport because
42649           // this implicitly changes the currently selected bubble/sequence
42650           setStyles: function (context, hovered, reset) {
42651             if (reset) {  // reset all layers
42652               context.container().selectAll('.viewfield-group')
42653                 .classed('highlighted', false)
42654                 .classed('hovered', false)
42655                 .classed('currentView', false);
42656
42657               context.container().selectAll('.sequence')
42658                 .classed('highlighted', false)
42659                 .classed('currentView', false);
42660             }
42661
42662             let hoveredBubbleKey = hovered && hovered.key;
42663             let hoveredSequenceKey = this.getSequenceKeyForBubble(hovered);
42664             let hoveredSequence = hoveredSequenceKey && _ssCache.sequences[hoveredSequenceKey];
42665             let hoveredBubbleKeys =  (hoveredSequence && hoveredSequence.bubbles.map(d => d.key)) || [];
42666
42667             let viewer = context.container().select('.photoviewer');
42668             let selected = viewer.empty() ? undefined : viewer.datum();
42669             let selectedBubbleKey = selected && selected.key;
42670             let selectedSequenceKey = this.getSequenceKeyForBubble(selected);
42671             let selectedSequence = selectedSequenceKey && _ssCache.sequences[selectedSequenceKey];
42672             let selectedBubbleKeys = (selectedSequence && selectedSequence.bubbles.map(d => d.key)) || [];
42673
42674             // highlight sibling viewfields on either the selected or the hovered sequences
42675             let highlightedBubbleKeys = utilArrayUnion(hoveredBubbleKeys, selectedBubbleKeys);
42676
42677             context.container().selectAll('.layer-streetside-images .viewfield-group')
42678               .classed('highlighted', d => highlightedBubbleKeys.indexOf(d.key) !== -1)
42679               .classed('hovered',     d => d.key === hoveredBubbleKey)
42680               .classed('currentView', d => d.key === selectedBubbleKey);
42681
42682             context.container().selectAll('.layer-streetside-images .sequence')
42683               .classed('highlighted', d => d.properties.key === hoveredSequenceKey)
42684               .classed('currentView', d => d.properties.key === selectedSequenceKey);
42685
42686             // update viewfields if needed
42687             context.container().selectAll('.viewfield-group .viewfield')
42688               .attr('d', viewfieldPath);
42689
42690             function viewfieldPath() {
42691               let d = this.parentNode.__data__;
42692               if (d.pano && d.key !== selectedBubbleKey) {
42693                 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
42694               } else {
42695                 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
42696               }
42697             }
42698
42699             return this;
42700           },
42701
42702
42703           /**
42704            * cache().
42705            */
42706           cache: function () {
42707             return _ssCache;
42708           }
42709         };
42710
42711         var apibase$4 = 'https://taginfo.openstreetmap.org/api/4/';
42712         var _inflight$2 = {};
42713         var _popularKeys = {};
42714         var _taginfoCache = {};
42715
42716         var tag_sorts = {
42717             point: 'count_nodes',
42718             vertex: 'count_nodes',
42719             area: 'count_ways',
42720             line: 'count_ways'
42721         };
42722         var tag_sort_members = {
42723             point: 'count_node_members',
42724             vertex: 'count_node_members',
42725             area: 'count_way_members',
42726             line: 'count_way_members',
42727             relation: 'count_relation_members'
42728         };
42729         var tag_filters = {
42730             point: 'nodes',
42731             vertex: 'nodes',
42732             area: 'ways',
42733             line: 'ways'
42734         };
42735         var tag_members_fractions = {
42736             point: 'count_node_members_fraction',
42737             vertex: 'count_node_members_fraction',
42738             area: 'count_way_members_fraction',
42739             line: 'count_way_members_fraction',
42740             relation: 'count_relation_members_fraction'
42741         };
42742
42743
42744         function sets(params, n, o) {
42745             if (params.geometry && o[params.geometry]) {
42746                 params[n] = o[params.geometry];
42747             }
42748             return params;
42749         }
42750
42751
42752         function setFilter(params) {
42753             return sets(params, 'filter', tag_filters);
42754         }
42755
42756
42757         function setSort(params) {
42758             return sets(params, 'sortname', tag_sorts);
42759         }
42760
42761
42762         function setSortMembers(params) {
42763             return sets(params, 'sortname', tag_sort_members);
42764         }
42765
42766
42767         function clean(params) {
42768             return utilObjectOmit(params, ['geometry', 'debounce']);
42769         }
42770
42771
42772         function filterKeys(type) {
42773             var count_type = type ? 'count_' + type : 'count_all';
42774             return function(d) {
42775                 return parseFloat(d[count_type]) > 2500 || d.in_wiki;
42776             };
42777         }
42778
42779
42780         function filterMultikeys(prefix) {
42781             return function(d) {
42782                 // d.key begins with prefix, and d.key contains no additional ':'s
42783                 var re = new RegExp('^' + prefix + '(.*)$');
42784                 var matches = d.key.match(re) || [];
42785                 return (matches.length === 2 && matches[1].indexOf(':') === -1);
42786             };
42787         }
42788
42789
42790         function filterValues(allowUpperCase) {
42791             return function(d) {
42792                 if (d.value.match(/[;,]/) !== null) return false;  // exclude some punctuation
42793                 if (!allowUpperCase && d.value.match(/[A-Z*]/) !== null) return false;  // exclude uppercase letters
42794                 return parseFloat(d.fraction) > 0.0;
42795             };
42796         }
42797
42798
42799         function filterRoles(geometry) {
42800             return function(d) {
42801                 if (d.role === '') return false; // exclude empty role
42802                 if (d.role.match(/[A-Z*;,]/) !== null) return false;  // exclude uppercase letters and some punctuation
42803                 return parseFloat(d[tag_members_fractions[geometry]]) > 0.0;
42804             };
42805         }
42806
42807
42808         function valKey(d) {
42809             return {
42810                 value: d.key,
42811                 title: d.key
42812             };
42813         }
42814
42815
42816         function valKeyDescription(d) {
42817             var obj = {
42818                 value: d.value,
42819                 title: d.description || d.value
42820             };
42821             if (d.count) {
42822                 obj.count = d.count;
42823             }
42824             return obj;
42825         }
42826
42827
42828         function roleKey(d) {
42829             return {
42830                 value: d.role,
42831                 title: d.role
42832             };
42833         }
42834
42835
42836         // sort keys with ':' lower than keys without ':'
42837         function sortKeys(a, b) {
42838             return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1
42839                 : (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1
42840                 : 0;
42841         }
42842
42843
42844         var debouncedRequest$1 = debounce(request$1, 300, { leading: false });
42845
42846         function request$1(url, params, exactMatch, callback, loaded) {
42847             if (_inflight$2[url]) return;
42848
42849             if (checkCache(url, params, exactMatch, callback)) return;
42850
42851             var controller = new AbortController();
42852             _inflight$2[url] = controller;
42853
42854             d3_json(url, { signal: controller.signal })
42855                 .then(function(result) {
42856                     delete _inflight$2[url];
42857                     if (loaded) loaded(null, result);
42858                 })
42859                 .catch(function(err) {
42860                     delete _inflight$2[url];
42861                     if (err.name === 'AbortError') return;
42862                     if (loaded) loaded(err.message);
42863                 });
42864         }
42865
42866
42867         function checkCache(url, params, exactMatch, callback) {
42868             var rp = params.rp || 25;
42869             var testQuery = params.query || '';
42870             var testUrl = url;
42871
42872             do {
42873                 var hit = _taginfoCache[testUrl];
42874
42875                 // exact match, or shorter match yielding fewer than max results (rp)
42876                 if (hit && (url === testUrl || hit.length < rp)) {
42877                     callback(null, hit);
42878                     return true;
42879                 }
42880
42881                 // don't try to shorten the query
42882                 if (exactMatch || !testQuery.length) return false;
42883
42884                 // do shorten the query to see if we already have a cached result
42885                 // that has returned fewer than max results (rp)
42886                 testQuery = testQuery.slice(0, -1);
42887                 testUrl = url.replace(/&query=(.*?)&/, '&query=' + testQuery + '&');
42888             } while (testQuery.length >= 0);
42889
42890             return false;
42891         }
42892
42893
42894         var serviceTaginfo = {
42895
42896             init: function() {
42897                 _inflight$2 = {};
42898                 _taginfoCache = {};
42899                 _popularKeys = {
42900                     // manually exclude some keys – #5377, #7485
42901                     postal_code: true,
42902                     full_name: true,
42903                     loc_name: true,
42904                     reg_name: true,
42905                     short_name: true,
42906                     sorting_name: true,
42907                     artist_name: true,
42908                     nat_name: true,
42909                     long_name: true,
42910                     'bridge:name': true
42911                 };
42912
42913                 // Fetch popular keys.  We'll exclude these from `values`
42914                 // lookups because they stress taginfo, and they aren't likely
42915                 // to yield meaningful autocomplete results.. see #3955
42916                 var params = {
42917                     rp: 100,
42918                     sortname: 'values_all',
42919                     sortorder: 'desc',
42920                     page: 1,
42921                     debounce: false,
42922                     lang: _mainLocalizer.languageCode()
42923                 };
42924                 this.keys(params, function(err, data) {
42925                     if (err) return;
42926                     data.forEach(function(d) {
42927                         if (d.value === 'opening_hours') return;  // exception
42928                         _popularKeys[d.value] = true;
42929                     });
42930                 });
42931             },
42932
42933
42934             reset: function() {
42935                 Object.values(_inflight$2).forEach(function(controller) { controller.abort(); });
42936                 _inflight$2 = {};
42937             },
42938
42939
42940             keys: function(params, callback) {
42941                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
42942                 params = clean(setSort(params));
42943                 params = Object.assign({
42944                     rp: 10,
42945                     sortname: 'count_all',
42946                     sortorder: 'desc',
42947                     page: 1,
42948                     lang: _mainLocalizer.languageCode()
42949                 }, params);
42950
42951                 var url = apibase$4 + 'keys/all?' + utilQsString(params);
42952                 doRequest(url, params, false, callback, function(err, d) {
42953                     if (err) {
42954                         callback(err);
42955                     } else {
42956                         var f = filterKeys(params.filter);
42957                         var result = d.data.filter(f).sort(sortKeys).map(valKey);
42958                         _taginfoCache[url] = result;
42959                         callback(null, result);
42960                     }
42961                 });
42962             },
42963
42964
42965             multikeys: function(params, callback) {
42966                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
42967                 params = clean(setSort(params));
42968                 params = Object.assign({
42969                     rp: 25,
42970                     sortname: 'count_all',
42971                     sortorder: 'desc',
42972                     page: 1,
42973                     lang: _mainLocalizer.languageCode()
42974                 }, params);
42975
42976                 var prefix = params.query;
42977                 var url = apibase$4 + 'keys/all?' + utilQsString(params);
42978                 doRequest(url, params, true, callback, function(err, d) {
42979                     if (err) {
42980                         callback(err);
42981                     } else {
42982                         var f = filterMultikeys(prefix);
42983                         var result = d.data.filter(f).map(valKey);
42984                         _taginfoCache[url] = result;
42985                         callback(null, result);
42986                     }
42987                 });
42988             },
42989
42990
42991             values: function(params, callback) {
42992                 // Exclude popular keys from values lookups.. see #3955
42993                 var key = params.key;
42994                 if (key && _popularKeys[key]) {
42995                     callback(null, []);
42996                     return;
42997                 }
42998
42999                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43000                 params = clean(setSort(setFilter(params)));
43001                 params = Object.assign({
43002                     rp: 25,
43003                     sortname: 'count_all',
43004                     sortorder: 'desc',
43005                     page: 1,
43006                     lang: _mainLocalizer.languageCode()
43007                 }, params);
43008
43009                 var url = apibase$4 + 'key/values?' + utilQsString(params);
43010                 doRequest(url, params, false, callback, function(err, d) {
43011                     if (err) {
43012                         callback(err);
43013                     } else {
43014                         // In most cases we prefer taginfo value results with lowercase letters.
43015                         // A few OSM keys expect values to contain uppercase values (see #3377).
43016                         // This is not an exhaustive list (e.g. `name` also has uppercase values)
43017                         // but these are the fields where taginfo value lookup is most useful.
43018                         var re = /network|taxon|genus|species|brand|grape_variety|royal_cypher|listed_status|booth|rating|stars|:output|_hours|_times|_ref|manufacturer|country|target|brewery/;
43019                         var allowUpperCase = re.test(params.key);
43020                         var f = filterValues(allowUpperCase);
43021
43022                         var result = d.data.filter(f).map(valKeyDescription);
43023                         _taginfoCache[url] = result;
43024                         callback(null, result);
43025                     }
43026                 });
43027             },
43028
43029
43030             roles: function(params, callback) {
43031                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43032                 var geometry = params.geometry;
43033                 params = clean(setSortMembers(params));
43034                 params = Object.assign({
43035                     rp: 25,
43036                     sortname: 'count_all_members',
43037                     sortorder: 'desc',
43038                     page: 1,
43039                     lang: _mainLocalizer.languageCode()
43040                 }, params);
43041
43042                 var url = apibase$4 + 'relation/roles?' + utilQsString(params);
43043                 doRequest(url, params, true, callback, function(err, d) {
43044                     if (err) {
43045                         callback(err);
43046                     } else {
43047                         var f = filterRoles(geometry);
43048                         var result = d.data.filter(f).map(roleKey);
43049                         _taginfoCache[url] = result;
43050                         callback(null, result);
43051                     }
43052                 });
43053             },
43054
43055
43056             docs: function(params, callback) {
43057                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43058                 params = clean(setSort(params));
43059
43060                 var path = 'key/wiki_pages?';
43061                 if (params.value) {
43062                     path = 'tag/wiki_pages?';
43063                 } else if (params.rtype) {
43064                     path = 'relation/wiki_pages?';
43065                 }
43066
43067                 var url = apibase$4 + path + utilQsString(params);
43068                 doRequest(url, params, true, callback, function(err, d) {
43069                     if (err) {
43070                         callback(err);
43071                     } else {
43072                         _taginfoCache[url] = d.data;
43073                         callback(null, d.data);
43074                     }
43075                 });
43076             },
43077
43078
43079             apibase: function(_) {
43080                 if (!arguments.length) return apibase$4;
43081                 apibase$4 = _;
43082                 return this;
43083             }
43084
43085         };
43086
43087         var helpers$1 = createCommonjsModule(function (module, exports) {
43088         Object.defineProperty(exports, "__esModule", { value: true });
43089         /**
43090          * @module helpers
43091          */
43092         /**
43093          * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth.
43094          *
43095          * @memberof helpers
43096          * @type {number}
43097          */
43098         exports.earthRadius = 6371008.8;
43099         /**
43100          * Unit of measurement factors using a spherical (non-ellipsoid) earth radius.
43101          *
43102          * @memberof helpers
43103          * @type {Object}
43104          */
43105         exports.factors = {
43106             centimeters: exports.earthRadius * 100,
43107             centimetres: exports.earthRadius * 100,
43108             degrees: exports.earthRadius / 111325,
43109             feet: exports.earthRadius * 3.28084,
43110             inches: exports.earthRadius * 39.370,
43111             kilometers: exports.earthRadius / 1000,
43112             kilometres: exports.earthRadius / 1000,
43113             meters: exports.earthRadius,
43114             metres: exports.earthRadius,
43115             miles: exports.earthRadius / 1609.344,
43116             millimeters: exports.earthRadius * 1000,
43117             millimetres: exports.earthRadius * 1000,
43118             nauticalmiles: exports.earthRadius / 1852,
43119             radians: 1,
43120             yards: exports.earthRadius / 1.0936,
43121         };
43122         /**
43123          * Units of measurement factors based on 1 meter.
43124          *
43125          * @memberof helpers
43126          * @type {Object}
43127          */
43128         exports.unitsFactors = {
43129             centimeters: 100,
43130             centimetres: 100,
43131             degrees: 1 / 111325,
43132             feet: 3.28084,
43133             inches: 39.370,
43134             kilometers: 1 / 1000,
43135             kilometres: 1 / 1000,
43136             meters: 1,
43137             metres: 1,
43138             miles: 1 / 1609.344,
43139             millimeters: 1000,
43140             millimetres: 1000,
43141             nauticalmiles: 1 / 1852,
43142             radians: 1 / exports.earthRadius,
43143             yards: 1 / 1.0936,
43144         };
43145         /**
43146          * Area of measurement factors based on 1 square meter.
43147          *
43148          * @memberof helpers
43149          * @type {Object}
43150          */
43151         exports.areaFactors = {
43152             acres: 0.000247105,
43153             centimeters: 10000,
43154             centimetres: 10000,
43155             feet: 10.763910417,
43156             inches: 1550.003100006,
43157             kilometers: 0.000001,
43158             kilometres: 0.000001,
43159             meters: 1,
43160             metres: 1,
43161             miles: 3.86e-7,
43162             millimeters: 1000000,
43163             millimetres: 1000000,
43164             yards: 1.195990046,
43165         };
43166         /**
43167          * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
43168          *
43169          * @name feature
43170          * @param {Geometry} geometry input geometry
43171          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43172          * @param {Object} [options={}] Optional Parameters
43173          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43174          * @param {string|number} [options.id] Identifier associated with the Feature
43175          * @returns {Feature} a GeoJSON Feature
43176          * @example
43177          * var geometry = {
43178          *   "type": "Point",
43179          *   "coordinates": [110, 50]
43180          * };
43181          *
43182          * var feature = turf.feature(geometry);
43183          *
43184          * //=feature
43185          */
43186         function feature(geom, properties, options) {
43187             if (options === void 0) { options = {}; }
43188             var feat = { type: "Feature" };
43189             if (options.id === 0 || options.id) {
43190                 feat.id = options.id;
43191             }
43192             if (options.bbox) {
43193                 feat.bbox = options.bbox;
43194             }
43195             feat.properties = properties || {};
43196             feat.geometry = geom;
43197             return feat;
43198         }
43199         exports.feature = feature;
43200         /**
43201          * Creates a GeoJSON {@link Geometry} from a Geometry string type & coordinates.
43202          * For GeometryCollection type use `helpers.geometryCollection`
43203          *
43204          * @name geometry
43205          * @param {string} type Geometry Type
43206          * @param {Array<any>} coordinates Coordinates
43207          * @param {Object} [options={}] Optional Parameters
43208          * @returns {Geometry} a GeoJSON Geometry
43209          * @example
43210          * var type = "Point";
43211          * var coordinates = [110, 50];
43212          * var geometry = turf.geometry(type, coordinates);
43213          * // => geometry
43214          */
43215         function geometry(type, coordinates, options) {
43216             switch (type) {
43217                 case "Point": return point(coordinates).geometry;
43218                 case "LineString": return lineString(coordinates).geometry;
43219                 case "Polygon": return polygon(coordinates).geometry;
43220                 case "MultiPoint": return multiPoint(coordinates).geometry;
43221                 case "MultiLineString": return multiLineString(coordinates).geometry;
43222                 case "MultiPolygon": return multiPolygon(coordinates).geometry;
43223                 default: throw new Error(type + " is invalid");
43224             }
43225         }
43226         exports.geometry = geometry;
43227         /**
43228          * Creates a {@link Point} {@link Feature} from a Position.
43229          *
43230          * @name point
43231          * @param {Array<number>} coordinates longitude, latitude position (each in decimal degrees)
43232          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43233          * @param {Object} [options={}] Optional Parameters
43234          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43235          * @param {string|number} [options.id] Identifier associated with the Feature
43236          * @returns {Feature<Point>} a Point feature
43237          * @example
43238          * var point = turf.point([-75.343, 39.984]);
43239          *
43240          * //=point
43241          */
43242         function point(coordinates, properties, options) {
43243             if (options === void 0) { options = {}; }
43244             var geom = {
43245                 type: "Point",
43246                 coordinates: coordinates,
43247             };
43248             return feature(geom, properties, options);
43249         }
43250         exports.point = point;
43251         /**
43252          * Creates a {@link Point} {@link FeatureCollection} from an Array of Point coordinates.
43253          *
43254          * @name points
43255          * @param {Array<Array<number>>} coordinates an array of Points
43256          * @param {Object} [properties={}] Translate these properties to each Feature
43257          * @param {Object} [options={}] Optional Parameters
43258          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
43259          * associated with the FeatureCollection
43260          * @param {string|number} [options.id] Identifier associated with the FeatureCollection
43261          * @returns {FeatureCollection<Point>} Point Feature
43262          * @example
43263          * var points = turf.points([
43264          *   [-75, 39],
43265          *   [-80, 45],
43266          *   [-78, 50]
43267          * ]);
43268          *
43269          * //=points
43270          */
43271         function points(coordinates, properties, options) {
43272             if (options === void 0) { options = {}; }
43273             return featureCollection(coordinates.map(function (coords) {
43274                 return point(coords, properties);
43275             }), options);
43276         }
43277         exports.points = points;
43278         /**
43279          * Creates a {@link Polygon} {@link Feature} from an Array of LinearRings.
43280          *
43281          * @name polygon
43282          * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
43283          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43284          * @param {Object} [options={}] Optional Parameters
43285          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43286          * @param {string|number} [options.id] Identifier associated with the Feature
43287          * @returns {Feature<Polygon>} Polygon Feature
43288          * @example
43289          * var polygon = turf.polygon([[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]], { name: 'poly1' });
43290          *
43291          * //=polygon
43292          */
43293         function polygon(coordinates, properties, options) {
43294             if (options === void 0) { options = {}; }
43295             for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) {
43296                 var ring = coordinates_1[_i];
43297                 if (ring.length < 4) {
43298                     throw new Error("Each LinearRing of a Polygon must have 4 or more Positions.");
43299                 }
43300                 for (var j = 0; j < ring[ring.length - 1].length; j++) {
43301                     // Check if first point of Polygon contains two numbers
43302                     if (ring[ring.length - 1][j] !== ring[0][j]) {
43303                         throw new Error("First and last Position are not equivalent.");
43304                     }
43305                 }
43306             }
43307             var geom = {
43308                 type: "Polygon",
43309                 coordinates: coordinates,
43310             };
43311             return feature(geom, properties, options);
43312         }
43313         exports.polygon = polygon;
43314         /**
43315          * Creates a {@link Polygon} {@link FeatureCollection} from an Array of Polygon coordinates.
43316          *
43317          * @name polygons
43318          * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygon coordinates
43319          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43320          * @param {Object} [options={}] Optional Parameters
43321          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43322          * @param {string|number} [options.id] Identifier associated with the FeatureCollection
43323          * @returns {FeatureCollection<Polygon>} Polygon FeatureCollection
43324          * @example
43325          * var polygons = turf.polygons([
43326          *   [[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]],
43327          *   [[[-15, 42], [-14, 46], [-12, 41], [-17, 44], [-15, 42]]],
43328          * ]);
43329          *
43330          * //=polygons
43331          */
43332         function polygons(coordinates, properties, options) {
43333             if (options === void 0) { options = {}; }
43334             return featureCollection(coordinates.map(function (coords) {
43335                 return polygon(coords, properties);
43336             }), options);
43337         }
43338         exports.polygons = polygons;
43339         /**
43340          * Creates a {@link LineString} {@link Feature} from an Array of Positions.
43341          *
43342          * @name lineString
43343          * @param {Array<Array<number>>} coordinates an array of Positions
43344          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43345          * @param {Object} [options={}] Optional Parameters
43346          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43347          * @param {string|number} [options.id] Identifier associated with the Feature
43348          * @returns {Feature<LineString>} LineString Feature
43349          * @example
43350          * var linestring1 = turf.lineString([[-24, 63], [-23, 60], [-25, 65], [-20, 69]], {name: 'line 1'});
43351          * var linestring2 = turf.lineString([[-14, 43], [-13, 40], [-15, 45], [-10, 49]], {name: 'line 2'});
43352          *
43353          * //=linestring1
43354          * //=linestring2
43355          */
43356         function lineString(coordinates, properties, options) {
43357             if (options === void 0) { options = {}; }
43358             if (coordinates.length < 2) {
43359                 throw new Error("coordinates must be an array of two or more positions");
43360             }
43361             var geom = {
43362                 type: "LineString",
43363                 coordinates: coordinates,
43364             };
43365             return feature(geom, properties, options);
43366         }
43367         exports.lineString = lineString;
43368         /**
43369          * Creates a {@link LineString} {@link FeatureCollection} from an Array of LineString coordinates.
43370          *
43371          * @name lineStrings
43372          * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
43373          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43374          * @param {Object} [options={}] Optional Parameters
43375          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
43376          * associated with the FeatureCollection
43377          * @param {string|number} [options.id] Identifier associated with the FeatureCollection
43378          * @returns {FeatureCollection<LineString>} LineString FeatureCollection
43379          * @example
43380          * var linestrings = turf.lineStrings([
43381          *   [[-24, 63], [-23, 60], [-25, 65], [-20, 69]],
43382          *   [[-14, 43], [-13, 40], [-15, 45], [-10, 49]]
43383          * ]);
43384          *
43385          * //=linestrings
43386          */
43387         function lineStrings(coordinates, properties, options) {
43388             if (options === void 0) { options = {}; }
43389             return featureCollection(coordinates.map(function (coords) {
43390                 return lineString(coords, properties);
43391             }), options);
43392         }
43393         exports.lineStrings = lineStrings;
43394         /**
43395          * Takes one or more {@link Feature|Features} and creates a {@link FeatureCollection}.
43396          *
43397          * @name featureCollection
43398          * @param {Feature[]} features input features
43399          * @param {Object} [options={}] Optional Parameters
43400          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43401          * @param {string|number} [options.id] Identifier associated with the Feature
43402          * @returns {FeatureCollection} FeatureCollection of Features
43403          * @example
43404          * var locationA = turf.point([-75.343, 39.984], {name: 'Location A'});
43405          * var locationB = turf.point([-75.833, 39.284], {name: 'Location B'});
43406          * var locationC = turf.point([-75.534, 39.123], {name: 'Location C'});
43407          *
43408          * var collection = turf.featureCollection([
43409          *   locationA,
43410          *   locationB,
43411          *   locationC
43412          * ]);
43413          *
43414          * //=collection
43415          */
43416         function featureCollection(features, options) {
43417             if (options === void 0) { options = {}; }
43418             var fc = { type: "FeatureCollection" };
43419             if (options.id) {
43420                 fc.id = options.id;
43421             }
43422             if (options.bbox) {
43423                 fc.bbox = options.bbox;
43424             }
43425             fc.features = features;
43426             return fc;
43427         }
43428         exports.featureCollection = featureCollection;
43429         /**
43430          * Creates a {@link Feature<MultiLineString>} based on a
43431          * coordinate array. Properties can be added optionally.
43432          *
43433          * @name multiLineString
43434          * @param {Array<Array<Array<number>>>} coordinates an array of LineStrings
43435          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43436          * @param {Object} [options={}] Optional Parameters
43437          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43438          * @param {string|number} [options.id] Identifier associated with the Feature
43439          * @returns {Feature<MultiLineString>} a MultiLineString feature
43440          * @throws {Error} if no coordinates are passed
43441          * @example
43442          * var multiLine = turf.multiLineString([[[0,0],[10,10]]]);
43443          *
43444          * //=multiLine
43445          */
43446         function multiLineString(coordinates, properties, options) {
43447             if (options === void 0) { options = {}; }
43448             var geom = {
43449                 type: "MultiLineString",
43450                 coordinates: coordinates,
43451             };
43452             return feature(geom, properties, options);
43453         }
43454         exports.multiLineString = multiLineString;
43455         /**
43456          * Creates a {@link Feature<MultiPoint>} based on a
43457          * coordinate array. Properties can be added optionally.
43458          *
43459          * @name multiPoint
43460          * @param {Array<Array<number>>} coordinates an array of Positions
43461          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43462          * @param {Object} [options={}] Optional Parameters
43463          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43464          * @param {string|number} [options.id] Identifier associated with the Feature
43465          * @returns {Feature<MultiPoint>} a MultiPoint feature
43466          * @throws {Error} if no coordinates are passed
43467          * @example
43468          * var multiPt = turf.multiPoint([[0,0],[10,10]]);
43469          *
43470          * //=multiPt
43471          */
43472         function multiPoint(coordinates, properties, options) {
43473             if (options === void 0) { options = {}; }
43474             var geom = {
43475                 type: "MultiPoint",
43476                 coordinates: coordinates,
43477             };
43478             return feature(geom, properties, options);
43479         }
43480         exports.multiPoint = multiPoint;
43481         /**
43482          * Creates a {@link Feature<MultiPolygon>} based on a
43483          * coordinate array. Properties can be added optionally.
43484          *
43485          * @name multiPolygon
43486          * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygons
43487          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43488          * @param {Object} [options={}] Optional Parameters
43489          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43490          * @param {string|number} [options.id] Identifier associated with the Feature
43491          * @returns {Feature<MultiPolygon>} a multipolygon feature
43492          * @throws {Error} if no coordinates are passed
43493          * @example
43494          * var multiPoly = turf.multiPolygon([[[[0,0],[0,10],[10,10],[10,0],[0,0]]]]);
43495          *
43496          * //=multiPoly
43497          *
43498          */
43499         function multiPolygon(coordinates, properties, options) {
43500             if (options === void 0) { options = {}; }
43501             var geom = {
43502                 type: "MultiPolygon",
43503                 coordinates: coordinates,
43504             };
43505             return feature(geom, properties, options);
43506         }
43507         exports.multiPolygon = multiPolygon;
43508         /**
43509          * Creates a {@link Feature<GeometryCollection>} based on a
43510          * coordinate array. Properties can be added optionally.
43511          *
43512          * @name geometryCollection
43513          * @param {Array<Geometry>} geometries an array of GeoJSON Geometries
43514          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43515          * @param {Object} [options={}] Optional Parameters
43516          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43517          * @param {string|number} [options.id] Identifier associated with the Feature
43518          * @returns {Feature<GeometryCollection>} a GeoJSON GeometryCollection Feature
43519          * @example
43520          * var pt = turf.geometry("Point", [100, 0]);
43521          * var line = turf.geometry("LineString", [[101, 0], [102, 1]]);
43522          * var collection = turf.geometryCollection([pt, line]);
43523          *
43524          * // => collection
43525          */
43526         function geometryCollection(geometries, properties, options) {
43527             if (options === void 0) { options = {}; }
43528             var geom = {
43529                 type: "GeometryCollection",
43530                 geometries: geometries,
43531             };
43532             return feature(geom, properties, options);
43533         }
43534         exports.geometryCollection = geometryCollection;
43535         /**
43536          * Round number to precision
43537          *
43538          * @param {number} num Number
43539          * @param {number} [precision=0] Precision
43540          * @returns {number} rounded number
43541          * @example
43542          * turf.round(120.4321)
43543          * //=120
43544          *
43545          * turf.round(120.4321, 2)
43546          * //=120.43
43547          */
43548         function round(num, precision) {
43549             if (precision === void 0) { precision = 0; }
43550             if (precision && !(precision >= 0)) {
43551                 throw new Error("precision must be a positive number");
43552             }
43553             var multiplier = Math.pow(10, precision || 0);
43554             return Math.round(num * multiplier) / multiplier;
43555         }
43556         exports.round = round;
43557         /**
43558          * Convert a distance measurement (assuming a spherical Earth) from radians to a more friendly unit.
43559          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
43560          *
43561          * @name radiansToLength
43562          * @param {number} radians in radians across the sphere
43563          * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
43564          * meters, kilometres, kilometers.
43565          * @returns {number} distance
43566          */
43567         function radiansToLength(radians, units) {
43568             if (units === void 0) { units = "kilometers"; }
43569             var factor = exports.factors[units];
43570             if (!factor) {
43571                 throw new Error(units + " units is invalid");
43572             }
43573             return radians * factor;
43574         }
43575         exports.radiansToLength = radiansToLength;
43576         /**
43577          * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into radians
43578          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
43579          *
43580          * @name lengthToRadians
43581          * @param {number} distance in real units
43582          * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
43583          * meters, kilometres, kilometers.
43584          * @returns {number} radians
43585          */
43586         function lengthToRadians(distance, units) {
43587             if (units === void 0) { units = "kilometers"; }
43588             var factor = exports.factors[units];
43589             if (!factor) {
43590                 throw new Error(units + " units is invalid");
43591             }
43592             return distance / factor;
43593         }
43594         exports.lengthToRadians = lengthToRadians;
43595         /**
43596          * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into degrees
43597          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, centimeters, kilometres, feet
43598          *
43599          * @name lengthToDegrees
43600          * @param {number} distance in real units
43601          * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
43602          * meters, kilometres, kilometers.
43603          * @returns {number} degrees
43604          */
43605         function lengthToDegrees(distance, units) {
43606             return radiansToDegrees(lengthToRadians(distance, units));
43607         }
43608         exports.lengthToDegrees = lengthToDegrees;
43609         /**
43610          * Converts any bearing angle from the north line direction (positive clockwise)
43611          * and returns an angle between 0-360 degrees (positive clockwise), 0 being the north line
43612          *
43613          * @name bearingToAzimuth
43614          * @param {number} bearing angle, between -180 and +180 degrees
43615          * @returns {number} angle between 0 and 360 degrees
43616          */
43617         function bearingToAzimuth(bearing) {
43618             var angle = bearing % 360;
43619             if (angle < 0) {
43620                 angle += 360;
43621             }
43622             return angle;
43623         }
43624         exports.bearingToAzimuth = bearingToAzimuth;
43625         /**
43626          * Converts an angle in radians to degrees
43627          *
43628          * @name radiansToDegrees
43629          * @param {number} radians angle in radians
43630          * @returns {number} degrees between 0 and 360 degrees
43631          */
43632         function radiansToDegrees(radians) {
43633             var degrees = radians % (2 * Math.PI);
43634             return degrees * 180 / Math.PI;
43635         }
43636         exports.radiansToDegrees = radiansToDegrees;
43637         /**
43638          * Converts an angle in degrees to radians
43639          *
43640          * @name degreesToRadians
43641          * @param {number} degrees angle between 0 and 360 degrees
43642          * @returns {number} angle in radians
43643          */
43644         function degreesToRadians(degrees) {
43645             var radians = degrees % 360;
43646             return radians * Math.PI / 180;
43647         }
43648         exports.degreesToRadians = degreesToRadians;
43649         /**
43650          * Converts a length to the requested unit.
43651          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
43652          *
43653          * @param {number} length to be converted
43654          * @param {Units} [originalUnit="kilometers"] of the length
43655          * @param {Units} [finalUnit="kilometers"] returned unit
43656          * @returns {number} the converted length
43657          */
43658         function convertLength(length, originalUnit, finalUnit) {
43659             if (originalUnit === void 0) { originalUnit = "kilometers"; }
43660             if (finalUnit === void 0) { finalUnit = "kilometers"; }
43661             if (!(length >= 0)) {
43662                 throw new Error("length must be a positive number");
43663             }
43664             return radiansToLength(lengthToRadians(length, originalUnit), finalUnit);
43665         }
43666         exports.convertLength = convertLength;
43667         /**
43668          * Converts a area to the requested unit.
43669          * Valid units: kilometers, kilometres, meters, metres, centimetres, millimeters, acres, miles, yards, feet, inches
43670          * @param {number} area to be converted
43671          * @param {Units} [originalUnit="meters"] of the distance
43672          * @param {Units} [finalUnit="kilometers"] returned unit
43673          * @returns {number} the converted distance
43674          */
43675         function convertArea(area, originalUnit, finalUnit) {
43676             if (originalUnit === void 0) { originalUnit = "meters"; }
43677             if (finalUnit === void 0) { finalUnit = "kilometers"; }
43678             if (!(area >= 0)) {
43679                 throw new Error("area must be a positive number");
43680             }
43681             var startFactor = exports.areaFactors[originalUnit];
43682             if (!startFactor) {
43683                 throw new Error("invalid original units");
43684             }
43685             var finalFactor = exports.areaFactors[finalUnit];
43686             if (!finalFactor) {
43687                 throw new Error("invalid final units");
43688             }
43689             return (area / startFactor) * finalFactor;
43690         }
43691         exports.convertArea = convertArea;
43692         /**
43693          * isNumber
43694          *
43695          * @param {*} num Number to validate
43696          * @returns {boolean} true/false
43697          * @example
43698          * turf.isNumber(123)
43699          * //=true
43700          * turf.isNumber('foo')
43701          * //=false
43702          */
43703         function isNumber(num) {
43704             return !isNaN(num) && num !== null && !Array.isArray(num) && !/^\s*$/.test(num);
43705         }
43706         exports.isNumber = isNumber;
43707         /**
43708          * isObject
43709          *
43710          * @param {*} input variable to validate
43711          * @returns {boolean} true/false
43712          * @example
43713          * turf.isObject({elevation: 10})
43714          * //=true
43715          * turf.isObject('foo')
43716          * //=false
43717          */
43718         function isObject(input) {
43719             return (!!input) && (input.constructor === Object);
43720         }
43721         exports.isObject = isObject;
43722         /**
43723          * Validate BBox
43724          *
43725          * @private
43726          * @param {Array<number>} bbox BBox to validate
43727          * @returns {void}
43728          * @throws Error if BBox is not valid
43729          * @example
43730          * validateBBox([-180, -40, 110, 50])
43731          * //=OK
43732          * validateBBox([-180, -40])
43733          * //=Error
43734          * validateBBox('Foo')
43735          * //=Error
43736          * validateBBox(5)
43737          * //=Error
43738          * validateBBox(null)
43739          * //=Error
43740          * validateBBox(undefined)
43741          * //=Error
43742          */
43743         function validateBBox(bbox) {
43744             if (!bbox) {
43745                 throw new Error("bbox is required");
43746             }
43747             if (!Array.isArray(bbox)) {
43748                 throw new Error("bbox must be an Array");
43749             }
43750             if (bbox.length !== 4 && bbox.length !== 6) {
43751                 throw new Error("bbox must be an Array of 4 or 6 numbers");
43752             }
43753             bbox.forEach(function (num) {
43754                 if (!isNumber(num)) {
43755                     throw new Error("bbox must only contain numbers");
43756                 }
43757             });
43758         }
43759         exports.validateBBox = validateBBox;
43760         /**
43761          * Validate Id
43762          *
43763          * @private
43764          * @param {string|number} id Id to validate
43765          * @returns {void}
43766          * @throws Error if Id is not valid
43767          * @example
43768          * validateId([-180, -40, 110, 50])
43769          * //=Error
43770          * validateId([-180, -40])
43771          * //=Error
43772          * validateId('Foo')
43773          * //=OK
43774          * validateId(5)
43775          * //=OK
43776          * validateId(null)
43777          * //=Error
43778          * validateId(undefined)
43779          * //=Error
43780          */
43781         function validateId(id) {
43782             if (!id) {
43783                 throw new Error("id is required");
43784             }
43785             if (["string", "number"].indexOf(typeof id) === -1) {
43786                 throw new Error("id must be a number or a string");
43787             }
43788         }
43789         exports.validateId = validateId;
43790         // Deprecated methods
43791         function radians2degrees() {
43792             throw new Error("method has been renamed to `radiansToDegrees`");
43793         }
43794         exports.radians2degrees = radians2degrees;
43795         function degrees2radians() {
43796             throw new Error("method has been renamed to `degreesToRadians`");
43797         }
43798         exports.degrees2radians = degrees2radians;
43799         function distanceToDegrees() {
43800             throw new Error("method has been renamed to `lengthToDegrees`");
43801         }
43802         exports.distanceToDegrees = distanceToDegrees;
43803         function distanceToRadians() {
43804             throw new Error("method has been renamed to `lengthToRadians`");
43805         }
43806         exports.distanceToRadians = distanceToRadians;
43807         function radiansToDistance() {
43808             throw new Error("method has been renamed to `radiansToLength`");
43809         }
43810         exports.radiansToDistance = radiansToDistance;
43811         function bearingToAngle() {
43812             throw new Error("method has been renamed to `bearingToAzimuth`");
43813         }
43814         exports.bearingToAngle = bearingToAngle;
43815         function convertDistance() {
43816             throw new Error("method has been renamed to `convertLength`");
43817         }
43818         exports.convertDistance = convertDistance;
43819         });
43820
43821         var invariant = createCommonjsModule(function (module, exports) {
43822         Object.defineProperty(exports, "__esModule", { value: true });
43823
43824         /**
43825          * Unwrap a coordinate from a Point Feature, Geometry or a single coordinate.
43826          *
43827          * @name getCoord
43828          * @param {Array<number>|Geometry<Point>|Feature<Point>} coord GeoJSON Point or an Array of numbers
43829          * @returns {Array<number>} coordinates
43830          * @example
43831          * var pt = turf.point([10, 10]);
43832          *
43833          * var coord = turf.getCoord(pt);
43834          * //= [10, 10]
43835          */
43836         function getCoord(coord) {
43837             if (!coord) {
43838                 throw new Error("coord is required");
43839             }
43840             if (!Array.isArray(coord)) {
43841                 if (coord.type === "Feature" && coord.geometry !== null && coord.geometry.type === "Point") {
43842                     return coord.geometry.coordinates;
43843                 }
43844                 if (coord.type === "Point") {
43845                     return coord.coordinates;
43846                 }
43847             }
43848             if (Array.isArray(coord) && coord.length >= 2 && !Array.isArray(coord[0]) && !Array.isArray(coord[1])) {
43849                 return coord;
43850             }
43851             throw new Error("coord must be GeoJSON Point or an Array of numbers");
43852         }
43853         exports.getCoord = getCoord;
43854         /**
43855          * Unwrap coordinates from a Feature, Geometry Object or an Array
43856          *
43857          * @name getCoords
43858          * @param {Array<any>|Geometry|Feature} coords Feature, Geometry Object or an Array
43859          * @returns {Array<any>} coordinates
43860          * @example
43861          * var poly = turf.polygon([[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]);
43862          *
43863          * var coords = turf.getCoords(poly);
43864          * //= [[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]
43865          */
43866         function getCoords(coords) {
43867             if (Array.isArray(coords)) {
43868                 return coords;
43869             }
43870             // Feature
43871             if (coords.type === "Feature") {
43872                 if (coords.geometry !== null) {
43873                     return coords.geometry.coordinates;
43874                 }
43875             }
43876             else {
43877                 // Geometry
43878                 if (coords.coordinates) {
43879                     return coords.coordinates;
43880                 }
43881             }
43882             throw new Error("coords must be GeoJSON Feature, Geometry Object or an Array");
43883         }
43884         exports.getCoords = getCoords;
43885         /**
43886          * Checks if coordinates contains a number
43887          *
43888          * @name containsNumber
43889          * @param {Array<any>} coordinates GeoJSON Coordinates
43890          * @returns {boolean} true if Array contains a number
43891          */
43892         function containsNumber(coordinates) {
43893             if (coordinates.length > 1 && helpers$1.isNumber(coordinates[0]) && helpers$1.isNumber(coordinates[1])) {
43894                 return true;
43895             }
43896             if (Array.isArray(coordinates[0]) && coordinates[0].length) {
43897                 return containsNumber(coordinates[0]);
43898             }
43899             throw new Error("coordinates must only contain numbers");
43900         }
43901         exports.containsNumber = containsNumber;
43902         /**
43903          * Enforce expectations about types of GeoJSON objects for Turf.
43904          *
43905          * @name geojsonType
43906          * @param {GeoJSON} value any GeoJSON object
43907          * @param {string} type expected GeoJSON type
43908          * @param {string} name name of calling function
43909          * @throws {Error} if value is not the expected type.
43910          */
43911         function geojsonType(value, type, name) {
43912             if (!type || !name) {
43913                 throw new Error("type and name required");
43914             }
43915             if (!value || value.type !== type) {
43916                 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + value.type);
43917             }
43918         }
43919         exports.geojsonType = geojsonType;
43920         /**
43921          * Enforce expectations about types of {@link Feature} inputs for Turf.
43922          * Internally this uses {@link geojsonType} to judge geometry types.
43923          *
43924          * @name featureOf
43925          * @param {Feature} feature a feature with an expected geometry type
43926          * @param {string} type expected GeoJSON type
43927          * @param {string} name name of calling function
43928          * @throws {Error} error if value is not the expected type.
43929          */
43930         function featureOf(feature, type, name) {
43931             if (!feature) {
43932                 throw new Error("No feature passed");
43933             }
43934             if (!name) {
43935                 throw new Error(".featureOf() requires a name");
43936             }
43937             if (!feature || feature.type !== "Feature" || !feature.geometry) {
43938                 throw new Error("Invalid input to " + name + ", Feature with geometry required");
43939             }
43940             if (!feature.geometry || feature.geometry.type !== type) {
43941                 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type);
43942             }
43943         }
43944         exports.featureOf = featureOf;
43945         /**
43946          * Enforce expectations about types of {@link FeatureCollection} inputs for Turf.
43947          * Internally this uses {@link geojsonType} to judge geometry types.
43948          *
43949          * @name collectionOf
43950          * @param {FeatureCollection} featureCollection a FeatureCollection for which features will be judged
43951          * @param {string} type expected GeoJSON type
43952          * @param {string} name name of calling function
43953          * @throws {Error} if value is not the expected type.
43954          */
43955         function collectionOf(featureCollection, type, name) {
43956             if (!featureCollection) {
43957                 throw new Error("No featureCollection passed");
43958             }
43959             if (!name) {
43960                 throw new Error(".collectionOf() requires a name");
43961             }
43962             if (!featureCollection || featureCollection.type !== "FeatureCollection") {
43963                 throw new Error("Invalid input to " + name + ", FeatureCollection required");
43964             }
43965             for (var _i = 0, _a = featureCollection.features; _i < _a.length; _i++) {
43966                 var feature = _a[_i];
43967                 if (!feature || feature.type !== "Feature" || !feature.geometry) {
43968                     throw new Error("Invalid input to " + name + ", Feature with geometry required");
43969                 }
43970                 if (!feature.geometry || feature.geometry.type !== type) {
43971                     throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type);
43972                 }
43973             }
43974         }
43975         exports.collectionOf = collectionOf;
43976         /**
43977          * Get Geometry from Feature or Geometry Object
43978          *
43979          * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
43980          * @returns {Geometry|null} GeoJSON Geometry Object
43981          * @throws {Error} if geojson is not a Feature or Geometry Object
43982          * @example
43983          * var point = {
43984          *   "type": "Feature",
43985          *   "properties": {},
43986          *   "geometry": {
43987          *     "type": "Point",
43988          *     "coordinates": [110, 40]
43989          *   }
43990          * }
43991          * var geom = turf.getGeom(point)
43992          * //={"type": "Point", "coordinates": [110, 40]}
43993          */
43994         function getGeom(geojson) {
43995             if (geojson.type === "Feature") {
43996                 return geojson.geometry;
43997             }
43998             return geojson;
43999         }
44000         exports.getGeom = getGeom;
44001         /**
44002          * Get GeoJSON object's type, Geometry type is prioritize.
44003          *
44004          * @param {GeoJSON} geojson GeoJSON object
44005          * @param {string} [name="geojson"] name of the variable to display in error message
44006          * @returns {string} GeoJSON type
44007          * @example
44008          * var point = {
44009          *   "type": "Feature",
44010          *   "properties": {},
44011          *   "geometry": {
44012          *     "type": "Point",
44013          *     "coordinates": [110, 40]
44014          *   }
44015          * }
44016          * var geom = turf.getType(point)
44017          * //="Point"
44018          */
44019         function getType(geojson, name) {
44020             if (geojson.type === "FeatureCollection") {
44021                 return "FeatureCollection";
44022             }
44023             if (geojson.type === "GeometryCollection") {
44024                 return "GeometryCollection";
44025             }
44026             if (geojson.type === "Feature" && geojson.geometry !== null) {
44027                 return geojson.geometry.type;
44028             }
44029             return geojson.type;
44030         }
44031         exports.getType = getType;
44032         });
44033
44034         var lineclip_1 = lineclip;
44035         var _default = lineclip;
44036
44037         lineclip.polyline = lineclip;
44038         lineclip.polygon = polygonclip;
44039
44040
44041         // Cohen-Sutherland line clippign algorithm, adapted to efficiently
44042         // handle polylines rather than just segments
44043
44044         function lineclip(points, bbox, result) {
44045
44046             var len = points.length,
44047                 codeA = bitCode(points[0], bbox),
44048                 part = [],
44049                 i, a, b, codeB, lastCode;
44050
44051             if (!result) result = [];
44052
44053             for (i = 1; i < len; i++) {
44054                 a = points[i - 1];
44055                 b = points[i];
44056                 codeB = lastCode = bitCode(b, bbox);
44057
44058                 while (true) {
44059
44060                     if (!(codeA | codeB)) { // accept
44061                         part.push(a);
44062
44063                         if (codeB !== lastCode) { // segment went outside
44064                             part.push(b);
44065
44066                             if (i < len - 1) { // start a new line
44067                                 result.push(part);
44068                                 part = [];
44069                             }
44070                         } else if (i === len - 1) {
44071                             part.push(b);
44072                         }
44073                         break;
44074
44075                     } else if (codeA & codeB) { // trivial reject
44076                         break;
44077
44078                     } else if (codeA) { // a outside, intersect with clip edge
44079                         a = intersect(a, b, codeA, bbox);
44080                         codeA = bitCode(a, bbox);
44081
44082                     } else { // b outside
44083                         b = intersect(a, b, codeB, bbox);
44084                         codeB = bitCode(b, bbox);
44085                     }
44086                 }
44087
44088                 codeA = lastCode;
44089             }
44090
44091             if (part.length) result.push(part);
44092
44093             return result;
44094         }
44095
44096         // Sutherland-Hodgeman polygon clipping algorithm
44097
44098         function polygonclip(points, bbox) {
44099
44100             var result, edge, prev, prevInside, i, p, inside;
44101
44102             // clip against each side of the clip rectangle
44103             for (edge = 1; edge <= 8; edge *= 2) {
44104                 result = [];
44105                 prev = points[points.length - 1];
44106                 prevInside = !(bitCode(prev, bbox) & edge);
44107
44108                 for (i = 0; i < points.length; i++) {
44109                     p = points[i];
44110                     inside = !(bitCode(p, bbox) & edge);
44111
44112                     // if segment goes through the clip window, add an intersection
44113                     if (inside !== prevInside) result.push(intersect(prev, p, edge, bbox));
44114
44115                     if (inside) result.push(p); // add a point if it's inside
44116
44117                     prev = p;
44118                     prevInside = inside;
44119                 }
44120
44121                 points = result;
44122
44123                 if (!points.length) break;
44124             }
44125
44126             return result;
44127         }
44128
44129         // intersect a segment against one of the 4 lines that make up the bbox
44130
44131         function intersect(a, b, edge, bbox) {
44132             return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
44133                    edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
44134                    edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
44135                    edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
44136                    null;
44137         }
44138
44139         // bit code reflects the point position relative to the bbox:
44140
44141         //         left  mid  right
44142         //    top  1001  1000  1010
44143         //    mid  0001  0000  0010
44144         // bottom  0101  0100  0110
44145
44146         function bitCode(p, bbox) {
44147             var code = 0;
44148
44149             if (p[0] < bbox[0]) code |= 1; // left
44150             else if (p[0] > bbox[2]) code |= 2; // right
44151
44152             if (p[1] < bbox[1]) code |= 4; // bottom
44153             else if (p[1] > bbox[3]) code |= 8; // top
44154
44155             return code;
44156         }
44157         lineclip_1.default = _default;
44158
44159         var bboxClip_1 = createCommonjsModule(function (module, exports) {
44160         var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) {
44161             if (mod && mod.__esModule) return mod;
44162             var result = {};
44163             if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
44164             result["default"] = mod;
44165             return result;
44166         };
44167         Object.defineProperty(exports, "__esModule", { value: true });
44168
44169
44170         var lineclip = __importStar(lineclip_1);
44171         /**
44172          * Takes a {@link Feature} and a bbox and clips the feature to the bbox using
44173          * [lineclip](https://github.com/mapbox/lineclip).
44174          * May result in degenerate edges when clipping Polygons.
44175          *
44176          * @name bboxClip
44177          * @param {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} feature feature to clip to the bbox
44178          * @param {BBox} bbox extent in [minX, minY, maxX, maxY] order
44179          * @returns {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} clipped Feature
44180          * @example
44181          * var bbox = [0, 0, 10, 10];
44182          * var poly = turf.polygon([[[2, 2], [8, 4], [12, 8], [3, 7], [2, 2]]]);
44183          *
44184          * var clipped = turf.bboxClip(poly, bbox);
44185          *
44186          * //addToMap
44187          * var addToMap = [bbox, poly, clipped]
44188          */
44189         function bboxClip(feature, bbox) {
44190             var geom = invariant.getGeom(feature);
44191             var type = geom.type;
44192             var properties = feature.type === "Feature" ? feature.properties : {};
44193             var coords = geom.coordinates;
44194             switch (type) {
44195                 case "LineString":
44196                 case "MultiLineString":
44197                     var lines_1 = [];
44198                     if (type === "LineString") {
44199                         coords = [coords];
44200                     }
44201                     coords.forEach(function (line) {
44202                         lineclip.polyline(line, bbox, lines_1);
44203                     });
44204                     if (lines_1.length === 1) {
44205                         return helpers$1.lineString(lines_1[0], properties);
44206                     }
44207                     return helpers$1.multiLineString(lines_1, properties);
44208                 case "Polygon":
44209                     return helpers$1.polygon(clipPolygon(coords, bbox), properties);
44210                 case "MultiPolygon":
44211                     return helpers$1.multiPolygon(coords.map(function (poly) {
44212                         return clipPolygon(poly, bbox);
44213                     }), properties);
44214                 default:
44215                     throw new Error("geometry " + type + " not supported");
44216             }
44217         }
44218         exports.default = bboxClip;
44219         function clipPolygon(rings, bbox) {
44220             var outRings = [];
44221             for (var _i = 0, rings_1 = rings; _i < rings_1.length; _i++) {
44222                 var ring = rings_1[_i];
44223                 var clipped = lineclip.polygon(ring, bbox);
44224                 if (clipped.length > 0) {
44225                     if (clipped[0][0] !== clipped[clipped.length - 1][0] || clipped[0][1] !== clipped[clipped.length - 1][1]) {
44226                         clipped.push(clipped[0]);
44227                     }
44228                     if (clipped.length >= 4) {
44229                         outRings.push(clipped);
44230                     }
44231                 }
44232             }
44233             return outRings;
44234         }
44235         });
44236
44237         var fastJsonStableStringify = function (data, opts) {
44238             if (!opts) opts = {};
44239             if (typeof opts === 'function') opts = { cmp: opts };
44240             var cycles = (typeof opts.cycles === 'boolean') ? opts.cycles : false;
44241
44242             var cmp = opts.cmp && (function (f) {
44243                 return function (node) {
44244                     return function (a, b) {
44245                         var aobj = { key: a, value: node[a] };
44246                         var bobj = { key: b, value: node[b] };
44247                         return f(aobj, bobj);
44248                     };
44249                 };
44250             })(opts.cmp);
44251
44252             var seen = [];
44253             return (function stringify (node) {
44254                 if (node && node.toJSON && typeof node.toJSON === 'function') {
44255                     node = node.toJSON();
44256                 }
44257
44258                 if (node === undefined) return;
44259                 if (typeof node == 'number') return isFinite(node) ? '' + node : 'null';
44260                 if (typeof node !== 'object') return JSON.stringify(node);
44261
44262                 var i, out;
44263                 if (Array.isArray(node)) {
44264                     out = '[';
44265                     for (i = 0; i < node.length; i++) {
44266                         if (i) out += ',';
44267                         out += stringify(node[i]) || 'null';
44268                     }
44269                     return out + ']';
44270                 }
44271
44272                 if (node === null) return 'null';
44273
44274                 if (seen.indexOf(node) !== -1) {
44275                     if (cycles) return JSON.stringify('__cycle__');
44276                     throw new TypeError('Converting circular structure to JSON');
44277                 }
44278
44279                 var seenIndex = seen.push(node) - 1;
44280                 var keys = Object.keys(node).sort(cmp && cmp(node));
44281                 out = '';
44282                 for (i = 0; i < keys.length; i++) {
44283                     var key = keys[i];
44284                     var value = stringify(node[key]);
44285
44286                     if (!value) continue;
44287                     if (out) out += ',';
44288                     out += JSON.stringify(key) + ':' + value;
44289                 }
44290                 seen.splice(seenIndex, 1);
44291                 return '{' + out + '}';
44292             })(data);
44293         };
44294
44295         function DEFAULT_COMPARE (a, b) { return a > b ? 1 : a < b ? -1 : 0; }
44296
44297         class SplayTree {
44298
44299           constructor(compare = DEFAULT_COMPARE, noDuplicates = false) {
44300             this._compare = compare;
44301             this._root = null;
44302             this._size = 0;
44303             this._noDuplicates = !!noDuplicates;
44304           }
44305
44306
44307           rotateLeft(x) {
44308             var y = x.right;
44309             if (y) {
44310               x.right = y.left;
44311               if (y.left) y.left.parent = x;
44312               y.parent = x.parent;
44313             }
44314
44315             if (!x.parent)                this._root = y;
44316             else if (x === x.parent.left) x.parent.left = y;
44317             else                          x.parent.right = y;
44318             if (y) y.left = x;
44319             x.parent = y;
44320           }
44321
44322
44323           rotateRight(x) {
44324             var y = x.left;
44325             if (y) {
44326               x.left = y.right;
44327               if (y.right) y.right.parent = x;
44328               y.parent = x.parent;
44329             }
44330
44331             if (!x.parent)               this._root = y;
44332             else if(x === x.parent.left) x.parent.left = y;
44333             else                         x.parent.right = y;
44334             if (y) y.right = x;
44335             x.parent = y;
44336           }
44337
44338
44339           _splay(x) {
44340             while (x.parent) {
44341               var p = x.parent;
44342               if (!p.parent) {
44343                 if (p.left === x) this.rotateRight(p);
44344                 else              this.rotateLeft(p);
44345               } else if (p.left === x && p.parent.left === p) {
44346                 this.rotateRight(p.parent);
44347                 this.rotateRight(p);
44348               } else if (p.right === x && p.parent.right === p) {
44349                 this.rotateLeft(p.parent);
44350                 this.rotateLeft(p);
44351               } else if (p.left === x && p.parent.right === p) {
44352                 this.rotateRight(p);
44353                 this.rotateLeft(p);
44354               } else {
44355                 this.rotateLeft(p);
44356                 this.rotateRight(p);
44357               }
44358             }
44359           }
44360
44361
44362           splay(x) {
44363             var p, gp, ggp, l, r;
44364
44365             while (x.parent) {
44366               p = x.parent;
44367               gp = p.parent;
44368
44369               if (gp && gp.parent) {
44370                 ggp = gp.parent;
44371                 if (ggp.left === gp) ggp.left  = x;
44372                 else                 ggp.right = x;
44373                 x.parent = ggp;
44374               } else {
44375                 x.parent = null;
44376                 this._root = x;
44377               }
44378
44379               l = x.left; r = x.right;
44380
44381               if (x === p.left) { // left
44382                 if (gp) {
44383                   if (gp.left === p) {
44384                     /* zig-zig */
44385                     if (p.right) {
44386                       gp.left = p.right;
44387                       gp.left.parent = gp;
44388                     } else gp.left = null;
44389
44390                     p.right   = gp;
44391                     gp.parent = p;
44392                   } else {
44393                     /* zig-zag */
44394                     if (l) {
44395                       gp.right = l;
44396                       l.parent = gp;
44397                     } else gp.right = null;
44398
44399                     x.left    = gp;
44400                     gp.parent = x;
44401                   }
44402                 }
44403                 if (r) {
44404                   p.left = r;
44405                   r.parent = p;
44406                 } else p.left = null;
44407
44408                 x.right  = p;
44409                 p.parent = x;
44410               } else { // right
44411                 if (gp) {
44412                   if (gp.right === p) {
44413                     /* zig-zig */
44414                     if (p.left) {
44415                       gp.right = p.left;
44416                       gp.right.parent = gp;
44417                     } else gp.right = null;
44418
44419                     p.left = gp;
44420                     gp.parent = p;
44421                   } else {
44422                     /* zig-zag */
44423                     if (r) {
44424                       gp.left = r;
44425                       r.parent = gp;
44426                     } else gp.left = null;
44427
44428                     x.right   = gp;
44429                     gp.parent = x;
44430                   }
44431                 }
44432                 if (l) {
44433                   p.right = l;
44434                   l.parent = p;
44435                 } else p.right = null;
44436
44437                 x.left   = p;
44438                 p.parent = x;
44439               }
44440             }
44441           }
44442
44443
44444           replace(u, v) {
44445             if (!u.parent) this._root = v;
44446             else if (u === u.parent.left) u.parent.left = v;
44447             else u.parent.right = v;
44448             if (v) v.parent = u.parent;
44449           }
44450
44451
44452           minNode(u = this._root) {
44453             if (u) while (u.left) u = u.left;
44454             return u;
44455           }
44456
44457
44458           maxNode(u = this._root) {
44459             if (u) while (u.right) u = u.right;
44460             return u;
44461           }
44462
44463
44464           insert(key, data) {
44465             var z = this._root;
44466             var p = null;
44467             var comp = this._compare;
44468             var cmp;
44469
44470             if (this._noDuplicates) {
44471               while (z) {
44472                 p = z;
44473                 cmp = comp(z.key, key);
44474                 if (cmp === 0) return;
44475                 else if (comp(z.key, key) < 0) z = z.right;
44476                 else z = z.left;
44477               }
44478             } else {
44479               while (z) {
44480                 p = z;
44481                 if (comp(z.key, key) < 0) z = z.right;
44482                 else z = z.left;
44483               }
44484             }
44485
44486             z = { key, data, left: null, right: null, parent: p };
44487
44488             if (!p)                          this._root = z;
44489             else if (comp(p.key, z.key) < 0) p.right = z;
44490             else                             p.left  = z;
44491
44492             this.splay(z);
44493             this._size++;
44494             return z;
44495           }
44496
44497
44498           find (key) {
44499             var z    = this._root;
44500             var comp = this._compare;
44501             while (z) {
44502               var cmp = comp(z.key, key);
44503               if      (cmp < 0) z = z.right;
44504               else if (cmp > 0) z = z.left;
44505               else              return z;
44506             }
44507             return null;
44508           }
44509
44510           /**
44511            * Whether the tree contains a node with the given key
44512            * @param  {Key} key
44513            * @return {boolean} true/false
44514            */
44515           contains (key) {
44516             var node       = this._root;
44517             var comparator = this._compare;
44518             while (node)  {
44519               var cmp = comparator(key, node.key);
44520               if      (cmp === 0) return true;
44521               else if (cmp < 0)   node = node.left;
44522               else                node = node.right;
44523             }
44524
44525             return false;
44526           }
44527
44528
44529           remove (key) {
44530             var z = this.find(key);
44531
44532             if (!z) return false;
44533
44534             this.splay(z);
44535
44536             if (!z.left) this.replace(z, z.right);
44537             else if (!z.right) this.replace(z, z.left);
44538             else {
44539               var y = this.minNode(z.right);
44540               if (y.parent !== z) {
44541                 this.replace(y, y.right);
44542                 y.right = z.right;
44543                 y.right.parent = y;
44544               }
44545               this.replace(z, y);
44546               y.left = z.left;
44547               y.left.parent = y;
44548             }
44549
44550             this._size--;
44551             return true;
44552           }
44553
44554
44555           removeNode(z) {
44556             if (!z) return false;
44557
44558             this.splay(z);
44559
44560             if (!z.left) this.replace(z, z.right);
44561             else if (!z.right) this.replace(z, z.left);
44562             else {
44563               var y = this.minNode(z.right);
44564               if (y.parent !== z) {
44565                 this.replace(y, y.right);
44566                 y.right = z.right;
44567                 y.right.parent = y;
44568               }
44569               this.replace(z, y);
44570               y.left = z.left;
44571               y.left.parent = y;
44572             }
44573
44574             this._size--;
44575             return true;
44576           }
44577
44578
44579           erase (key) {
44580             var z = this.find(key);
44581             if (!z) return;
44582
44583             this.splay(z);
44584
44585             var s = z.left;
44586             var t = z.right;
44587
44588             var sMax = null;
44589             if (s) {
44590               s.parent = null;
44591               sMax = this.maxNode(s);
44592               this.splay(sMax);
44593               this._root = sMax;
44594             }
44595             if (t) {
44596               if (s) sMax.right = t;
44597               else   this._root = t;
44598               t.parent = sMax;
44599             }
44600
44601             this._size--;
44602           }
44603
44604           /**
44605            * Removes and returns the node with smallest key
44606            * @return {?Node}
44607            */
44608           pop () {
44609             var node = this._root, returnValue = null;
44610             if (node) {
44611               while (node.left) node = node.left;
44612               returnValue = { key: node.key, data: node.data };
44613               this.remove(node.key);
44614             }
44615             return returnValue;
44616           }
44617
44618
44619           /* eslint-disable class-methods-use-this */
44620
44621           /**
44622            * Successor node
44623            * @param  {Node} node
44624            * @return {?Node}
44625            */
44626           next (node) {
44627             var successor = node;
44628             if (successor) {
44629               if (successor.right) {
44630                 successor = successor.right;
44631                 while (successor && successor.left) successor = successor.left;
44632               } else {
44633                 successor = node.parent;
44634                 while (successor && successor.right === node) {
44635                   node = successor; successor = successor.parent;
44636                 }
44637               }
44638             }
44639             return successor;
44640           }
44641
44642
44643           /**
44644            * Predecessor node
44645            * @param  {Node} node
44646            * @return {?Node}
44647            */
44648           prev (node) {
44649             var predecessor = node;
44650             if (predecessor) {
44651               if (predecessor.left) {
44652                 predecessor = predecessor.left;
44653                 while (predecessor && predecessor.right) predecessor = predecessor.right;
44654               } else {
44655                 predecessor = node.parent;
44656                 while (predecessor && predecessor.left === node) {
44657                   node = predecessor;
44658                   predecessor = predecessor.parent;
44659                 }
44660               }
44661             }
44662             return predecessor;
44663           }
44664           /* eslint-enable class-methods-use-this */
44665
44666
44667           /**
44668            * @param  {forEachCallback} callback
44669            * @return {SplayTree}
44670            */
44671           forEach(callback) {
44672             var current = this._root;
44673             var s = [], done = false, i = 0;
44674
44675             while (!done) {
44676               // Reach the left most Node of the current Node
44677               if (current) {
44678                 // Place pointer to a tree node on the stack
44679                 // before traversing the node's left subtree
44680                 s.push(current);
44681                 current = current.left;
44682               } else {
44683                 // BackTrack from the empty subtree and visit the Node
44684                 // at the top of the stack; however, if the stack is
44685                 // empty you are done
44686                 if (s.length > 0) {
44687                   current = s.pop();
44688                   callback(current, i++);
44689
44690                   // We have visited the node and its left
44691                   // subtree. Now, it's right subtree's turn
44692                   current = current.right;
44693                 } else done = true;
44694               }
44695             }
44696             return this;
44697           }
44698
44699
44700           /**
44701            * Walk key range from `low` to `high`. Stops if `fn` returns a value.
44702            * @param  {Key}      low
44703            * @param  {Key}      high
44704            * @param  {Function} fn
44705            * @param  {*?}       ctx
44706            * @return {SplayTree}
44707            */
44708           range(low, high, fn, ctx) {
44709             const Q = [];
44710             const compare = this._compare;
44711             let node = this._root, cmp;
44712
44713             while (Q.length !== 0 || node) {
44714               if (node) {
44715                 Q.push(node);
44716                 node = node.left;
44717               } else {
44718                 node = Q.pop();
44719                 cmp = compare(node.key, high);
44720                 if (cmp > 0) {
44721                   break;
44722                 } else if (compare(node.key, low) >= 0) {
44723                   if (fn.call(ctx, node)) return this; // stop if smth is returned
44724                 }
44725                 node = node.right;
44726               }
44727             }
44728             return this;
44729           }
44730
44731           /**
44732            * Returns all keys in order
44733            * @return {Array<Key>}
44734            */
44735           keys () {
44736             var current = this._root;
44737             var s = [], r = [], done = false;
44738
44739             while (!done) {
44740               if (current) {
44741                 s.push(current);
44742                 current = current.left;
44743               } else {
44744                 if (s.length > 0) {
44745                   current = s.pop();
44746                   r.push(current.key);
44747                   current = current.right;
44748                 } else done = true;
44749               }
44750             }
44751             return r;
44752           }
44753
44754
44755           /**
44756            * Returns `data` fields of all nodes in order.
44757            * @return {Array<Value>}
44758            */
44759           values () {
44760             var current = this._root;
44761             var s = [], r = [], done = false;
44762
44763             while (!done) {
44764               if (current) {
44765                 s.push(current);
44766                 current = current.left;
44767               } else {
44768                 if (s.length > 0) {
44769                   current = s.pop();
44770                   r.push(current.data);
44771                   current = current.right;
44772                 } else done = true;
44773               }
44774             }
44775             return r;
44776           }
44777
44778
44779           /**
44780            * Returns node at given index
44781            * @param  {number} index
44782            * @return {?Node}
44783            */
44784           at (index) {
44785             // removed after a consideration, more misleading than useful
44786             // index = index % this.size;
44787             // if (index < 0) index = this.size - index;
44788
44789             var current = this._root;
44790             var s = [], done = false, i = 0;
44791
44792             while (!done) {
44793               if (current) {
44794                 s.push(current);
44795                 current = current.left;
44796               } else {
44797                 if (s.length > 0) {
44798                   current = s.pop();
44799                   if (i === index) return current;
44800                   i++;
44801                   current = current.right;
44802                 } else done = true;
44803               }
44804             }
44805             return null;
44806           }
44807
44808           /**
44809            * Bulk-load items. Both array have to be same size
44810            * @param  {Array<Key>}    keys
44811            * @param  {Array<Value>}  [values]
44812            * @param  {Boolean}       [presort=false] Pre-sort keys and values, using
44813            *                                         tree's comparator. Sorting is done
44814            *                                         in-place
44815            * @return {AVLTree}
44816            */
44817           load(keys = [], values = [], presort = false) {
44818             if (this._size !== 0) throw new Error('bulk-load: tree is not empty');
44819             const size = keys.length;
44820             if (presort) sort(keys, values, 0, size - 1, this._compare);
44821             this._root = loadRecursive(null, keys, values, 0, size);
44822             this._size = size;
44823             return this;
44824           }
44825
44826
44827           min() {
44828             var node = this.minNode(this._root);
44829             if (node) return node.key;
44830             else      return null;
44831           }
44832
44833
44834           max() {
44835             var node = this.maxNode(this._root);
44836             if (node) return node.key;
44837             else      return null;
44838           }
44839
44840           isEmpty() { return this._root === null; }
44841           get size() { return this._size; }
44842
44843
44844           /**
44845            * Create a tree and load it with items
44846            * @param  {Array<Key>}          keys
44847            * @param  {Array<Value>?}        [values]
44848
44849            * @param  {Function?}            [comparator]
44850            * @param  {Boolean?}             [presort=false] Pre-sort keys and values, using
44851            *                                               tree's comparator. Sorting is done
44852            *                                               in-place
44853            * @param  {Boolean?}             [noDuplicates=false]   Allow duplicates
44854            * @return {SplayTree}
44855            */
44856           static createTree(keys, values, comparator, presort, noDuplicates) {
44857             return new SplayTree(comparator, noDuplicates).load(keys, values, presort);
44858           }
44859         }
44860
44861
44862         function loadRecursive (parent, keys, values, start, end) {
44863           const size = end - start;
44864           if (size > 0) {
44865             const middle = start + Math.floor(size / 2);
44866             const key    = keys[middle];
44867             const data   = values[middle];
44868             const node   = { key, data, parent };
44869             node.left    = loadRecursive(node, keys, values, start, middle);
44870             node.right   = loadRecursive(node, keys, values, middle + 1, end);
44871             return node;
44872           }
44873           return null;
44874         }
44875
44876
44877         function sort(keys, values, left, right, compare) {
44878           if (left >= right) return;
44879
44880           const pivot = keys[(left + right) >> 1];
44881           let i = left - 1;
44882           let j = right + 1;
44883
44884           while (true) {
44885             do i++; while (compare(keys[i], pivot) < 0);
44886             do j--; while (compare(keys[j], pivot) > 0);
44887             if (i >= j) break;
44888
44889             let tmp = keys[i];
44890             keys[i] = keys[j];
44891             keys[j] = tmp;
44892
44893             tmp = values[i];
44894             values[i] = values[j];
44895             values[j] = tmp;
44896           }
44897
44898           sort(keys, values,  left,     j, compare);
44899           sort(keys, values, j + 1, right, compare);
44900         }
44901
44902         const NORMAL               = 0;
44903         const NON_CONTRIBUTING     = 1;
44904         const SAME_TRANSITION      = 2;
44905         const DIFFERENT_TRANSITION = 3;
44906
44907         const INTERSECTION = 0;
44908         const UNION        = 1;
44909         const DIFFERENCE   = 2;
44910         const XOR          = 3;
44911
44912         /**
44913          * @param  {SweepEvent} event
44914          * @param  {SweepEvent} prev
44915          * @param  {Operation} operation
44916          */
44917         function computeFields (event, prev, operation) {
44918           // compute inOut and otherInOut fields
44919           if (prev === null) {
44920             event.inOut      = false;
44921             event.otherInOut = true;
44922
44923           // previous line segment in sweepline belongs to the same polygon
44924           } else {
44925             if (event.isSubject === prev.isSubject) {
44926               event.inOut      = !prev.inOut;
44927               event.otherInOut = prev.otherInOut;
44928
44929             // previous line segment in sweepline belongs to the clipping polygon
44930             } else {
44931               event.inOut      = !prev.otherInOut;
44932               event.otherInOut = prev.isVertical() ? !prev.inOut : prev.inOut;
44933             }
44934
44935             // compute prevInResult field
44936             if (prev) {
44937               event.prevInResult = (!inResult(prev, operation) || prev.isVertical())
44938                 ? prev.prevInResult : prev;
44939             }
44940           }
44941
44942           // check if the line segment belongs to the Boolean operation
44943           let isInResult = inResult(event, operation);
44944           if (isInResult) {
44945             event.resultTransition = determineResultTransition(event, operation);
44946           } else {
44947             event.resultTransition = 0;
44948           }
44949         }
44950
44951
44952         /* eslint-disable indent */
44953         function inResult(event, operation) {
44954           switch (event.type) {
44955             case NORMAL:
44956               switch (operation) {
44957                 case INTERSECTION:
44958                   return !event.otherInOut;
44959                 case UNION:
44960                   return event.otherInOut;
44961                 case DIFFERENCE:
44962                   // return (event.isSubject && !event.otherInOut) ||
44963                   //         (!event.isSubject && event.otherInOut);
44964                   return (event.isSubject && event.otherInOut) ||
44965                           (!event.isSubject && !event.otherInOut);
44966                 case XOR:
44967                   return true;
44968               }
44969               break;
44970             case SAME_TRANSITION:
44971               return operation === INTERSECTION || operation === UNION;
44972             case DIFFERENT_TRANSITION:
44973               return operation === DIFFERENCE;
44974             case NON_CONTRIBUTING:
44975               return false;
44976           }
44977           return false;
44978         }
44979         /* eslint-enable indent */
44980
44981
44982         function determineResultTransition(event, operation) {
44983           let thisIn = !event.inOut;
44984           let thatIn = !event.otherInOut;
44985
44986           let isIn;
44987           switch (operation) {
44988             case INTERSECTION:
44989               isIn = thisIn && thatIn; break;
44990             case UNION:
44991               isIn = thisIn || thatIn; break;
44992             case XOR:
44993               isIn = thisIn ^ thatIn; break;
44994             case DIFFERENCE:
44995               if (event.isSubject) {
44996                 isIn = thisIn && !thatIn;
44997               } else {
44998                 isIn = thatIn && !thisIn;
44999               }
45000               break;
45001           }
45002           return isIn ? +1 : -1;
45003         }
45004
45005         class SweepEvent {
45006
45007
45008           /**
45009            * Sweepline event
45010            *
45011            * @class {SweepEvent}
45012            * @param {Array.<Number>}  point
45013            * @param {Boolean}         left
45014            * @param {SweepEvent=}     otherEvent
45015            * @param {Boolean}         isSubject
45016            * @param {Number}          edgeType
45017            */
45018           constructor (point, left, otherEvent, isSubject, edgeType) {
45019
45020             /**
45021              * Is left endpoint?
45022              * @type {Boolean}
45023              */
45024             this.left = left;
45025
45026             /**
45027              * @type {Array.<Number>}
45028              */
45029             this.point = point;
45030
45031             /**
45032              * Other edge reference
45033              * @type {SweepEvent}
45034              */
45035             this.otherEvent = otherEvent;
45036
45037             /**
45038              * Belongs to source or clipping polygon
45039              * @type {Boolean}
45040              */
45041             this.isSubject = isSubject;
45042
45043             /**
45044              * Edge contribution type
45045              * @type {Number}
45046              */
45047             this.type = edgeType || NORMAL;
45048
45049
45050             /**
45051              * In-out transition for the sweepline crossing polygon
45052              * @type {Boolean}
45053              */
45054             this.inOut = false;
45055
45056
45057             /**
45058              * @type {Boolean}
45059              */
45060             this.otherInOut = false;
45061
45062             /**
45063              * Previous event in result?
45064              * @type {SweepEvent}
45065              */
45066             this.prevInResult = null;
45067
45068             /**
45069              * Type of result transition (0 = not in result, +1 = out-in, -1, in-out)
45070              * @type {Number}
45071              */
45072             this.resultTransition = 0;
45073
45074             // connection step
45075
45076             /**
45077              * @type {Number}
45078              */
45079             this.otherPos = -1;
45080
45081             /**
45082              * @type {Number}
45083              */
45084             this.outputContourId = -1;
45085
45086             this.isExteriorRing = true;   // TODO: Looks unused, remove?
45087           }
45088
45089
45090           /**
45091            * @param  {Array.<Number>}  p
45092            * @return {Boolean}
45093            */
45094           isBelow (p) {
45095             const p0 = this.point, p1 = this.otherEvent.point;
45096             return this.left
45097               ? (p0[0] - p[0]) * (p1[1] - p[1]) - (p1[0] - p[0]) * (p0[1] - p[1]) > 0
45098               // signedArea(this.point, this.otherEvent.point, p) > 0 :
45099               : (p1[0] - p[0]) * (p0[1] - p[1]) - (p0[0] - p[0]) * (p1[1] - p[1]) > 0;
45100               //signedArea(this.otherEvent.point, this.point, p) > 0;
45101           }
45102
45103
45104           /**
45105            * @param  {Array.<Number>}  p
45106            * @return {Boolean}
45107            */
45108           isAbove (p) {
45109             return !this.isBelow(p);
45110           }
45111
45112
45113           /**
45114            * @return {Boolean}
45115            */
45116           isVertical () {
45117             return this.point[0] === this.otherEvent.point[0];
45118           }
45119
45120
45121           /**
45122            * Does event belong to result?
45123            * @return {Boolean}
45124            */
45125           get inResult() {
45126             return this.resultTransition !== 0;
45127           }
45128
45129
45130           clone () {
45131             const copy = new SweepEvent(
45132               this.point, this.left, this.otherEvent, this.isSubject, this.type);
45133
45134             copy.contourId        = this.contourId;
45135             copy.resultTransition = this.resultTransition;
45136             copy.prevInResult     = this.prevInResult;
45137             copy.isExteriorRing   = this.isExteriorRing;
45138             copy.inOut            = this.inOut;
45139             copy.otherInOut       = this.otherInOut;
45140
45141             return copy;
45142           }
45143         }
45144
45145         function equals(p1, p2) {
45146           if (p1[0] === p2[0]) {
45147             if (p1[1] === p2[1]) {
45148               return true;
45149             } else {
45150               return false;
45151             }
45152           }
45153           return false;
45154         }
45155
45156         // const EPSILON = 1e-9;
45157         // const abs = Math.abs;
45158         // TODO https://github.com/w8r/martinez/issues/6#issuecomment-262847164
45159         // Precision problem.
45160         //
45161         // module.exports = function equals(p1, p2) {
45162         //   return abs(p1[0] - p2[0]) <= EPSILON && abs(p1[1] - p2[1]) <= EPSILON;
45163         // };
45164
45165         const epsilon$1 = 1.1102230246251565e-16;
45166         const splitter = 134217729;
45167         const resulterrbound = (3 + 8 * epsilon$1) * epsilon$1;
45168
45169         // fast_expansion_sum_zeroelim routine from oritinal code
45170         function sum$1(elen, e, flen, f, h) {
45171             let Q, Qnew, hh, bvirt;
45172             let enow = e[0];
45173             let fnow = f[0];
45174             let eindex = 0;
45175             let findex = 0;
45176             if ((fnow > enow) === (fnow > -enow)) {
45177                 Q = enow;
45178                 enow = e[++eindex];
45179             } else {
45180                 Q = fnow;
45181                 fnow = f[++findex];
45182             }
45183             let hindex = 0;
45184             if (eindex < elen && findex < flen) {
45185                 if ((fnow > enow) === (fnow > -enow)) {
45186                     Qnew = enow + Q;
45187                     hh = Q - (Qnew - enow);
45188                     enow = e[++eindex];
45189                 } else {
45190                     Qnew = fnow + Q;
45191                     hh = Q - (Qnew - fnow);
45192                     fnow = f[++findex];
45193                 }
45194                 Q = Qnew;
45195                 if (hh !== 0) {
45196                     h[hindex++] = hh;
45197                 }
45198                 while (eindex < elen && findex < flen) {
45199                     if ((fnow > enow) === (fnow > -enow)) {
45200                         Qnew = Q + enow;
45201                         bvirt = Qnew - Q;
45202                         hh = Q - (Qnew - bvirt) + (enow - bvirt);
45203                         enow = e[++eindex];
45204                     } else {
45205                         Qnew = Q + fnow;
45206                         bvirt = Qnew - Q;
45207                         hh = Q - (Qnew - bvirt) + (fnow - bvirt);
45208                         fnow = f[++findex];
45209                     }
45210                     Q = Qnew;
45211                     if (hh !== 0) {
45212                         h[hindex++] = hh;
45213                     }
45214                 }
45215             }
45216             while (eindex < elen) {
45217                 Qnew = Q + enow;
45218                 bvirt = Qnew - Q;
45219                 hh = Q - (Qnew - bvirt) + (enow - bvirt);
45220                 enow = e[++eindex];
45221                 Q = Qnew;
45222                 if (hh !== 0) {
45223                     h[hindex++] = hh;
45224                 }
45225             }
45226             while (findex < flen) {
45227                 Qnew = Q + fnow;
45228                 bvirt = Qnew - Q;
45229                 hh = Q - (Qnew - bvirt) + (fnow - bvirt);
45230                 fnow = f[++findex];
45231                 Q = Qnew;
45232                 if (hh !== 0) {
45233                     h[hindex++] = hh;
45234                 }
45235             }
45236             if (Q !== 0 || hindex === 0) {
45237                 h[hindex++] = Q;
45238             }
45239             return hindex;
45240         }
45241
45242         function estimate(elen, e) {
45243             let Q = e[0];
45244             for (let i = 1; i < elen; i++) Q += e[i];
45245             return Q;
45246         }
45247
45248         function vec(n) {
45249             return new Float64Array(n);
45250         }
45251
45252         const ccwerrboundA = (3 + 16 * epsilon$1) * epsilon$1;
45253         const ccwerrboundB = (2 + 12 * epsilon$1) * epsilon$1;
45254         const ccwerrboundC = (9 + 64 * epsilon$1) * epsilon$1 * epsilon$1;
45255
45256         const B = vec(4);
45257         const C1 = vec(8);
45258         const C2 = vec(12);
45259         const D = vec(16);
45260         const u = vec(4);
45261
45262         function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) {
45263             let acxtail, acytail, bcxtail, bcytail;
45264             let bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3;
45265
45266             const acx = ax - cx;
45267             const bcx = bx - cx;
45268             const acy = ay - cy;
45269             const bcy = by - cy;
45270
45271             s1 = acx * bcy;
45272             c = splitter * acx;
45273             ahi = c - (c - acx);
45274             alo = acx - ahi;
45275             c = splitter * bcy;
45276             bhi = c - (c - bcy);
45277             blo = bcy - bhi;
45278             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45279             t1 = acy * bcx;
45280             c = splitter * acy;
45281             ahi = c - (c - acy);
45282             alo = acy - ahi;
45283             c = splitter * bcx;
45284             bhi = c - (c - bcx);
45285             blo = bcx - bhi;
45286             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45287             _i = s0 - t0;
45288             bvirt = s0 - _i;
45289             B[0] = s0 - (_i + bvirt) + (bvirt - t0);
45290             _j = s1 + _i;
45291             bvirt = _j - s1;
45292             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45293             _i = _0 - t1;
45294             bvirt = _0 - _i;
45295             B[1] = _0 - (_i + bvirt) + (bvirt - t1);
45296             u3 = _j + _i;
45297             bvirt = u3 - _j;
45298             B[2] = _j - (u3 - bvirt) + (_i - bvirt);
45299             B[3] = u3;
45300
45301             let det = estimate(4, B);
45302             let errbound = ccwerrboundB * detsum;
45303             if (det >= errbound || -det >= errbound) {
45304                 return det;
45305             }
45306
45307             bvirt = ax - acx;
45308             acxtail = ax - (acx + bvirt) + (bvirt - cx);
45309             bvirt = bx - bcx;
45310             bcxtail = bx - (bcx + bvirt) + (bvirt - cx);
45311             bvirt = ay - acy;
45312             acytail = ay - (acy + bvirt) + (bvirt - cy);
45313             bvirt = by - bcy;
45314             bcytail = by - (bcy + bvirt) + (bvirt - cy);
45315
45316             if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) {
45317                 return det;
45318             }
45319
45320             errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det);
45321             det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail);
45322             if (det >= errbound || -det >= errbound) return det;
45323
45324             s1 = acxtail * bcy;
45325             c = splitter * acxtail;
45326             ahi = c - (c - acxtail);
45327             alo = acxtail - ahi;
45328             c = splitter * bcy;
45329             bhi = c - (c - bcy);
45330             blo = bcy - bhi;
45331             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45332             t1 = acytail * bcx;
45333             c = splitter * acytail;
45334             ahi = c - (c - acytail);
45335             alo = acytail - ahi;
45336             c = splitter * bcx;
45337             bhi = c - (c - bcx);
45338             blo = bcx - bhi;
45339             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45340             _i = s0 - t0;
45341             bvirt = s0 - _i;
45342             u[0] = s0 - (_i + bvirt) + (bvirt - t0);
45343             _j = s1 + _i;
45344             bvirt = _j - s1;
45345             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45346             _i = _0 - t1;
45347             bvirt = _0 - _i;
45348             u[1] = _0 - (_i + bvirt) + (bvirt - t1);
45349             u3 = _j + _i;
45350             bvirt = u3 - _j;
45351             u[2] = _j - (u3 - bvirt) + (_i - bvirt);
45352             u[3] = u3;
45353             const C1len = sum$1(4, B, 4, u, C1);
45354
45355             s1 = acx * bcytail;
45356             c = splitter * acx;
45357             ahi = c - (c - acx);
45358             alo = acx - ahi;
45359             c = splitter * bcytail;
45360             bhi = c - (c - bcytail);
45361             blo = bcytail - bhi;
45362             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45363             t1 = acy * bcxtail;
45364             c = splitter * acy;
45365             ahi = c - (c - acy);
45366             alo = acy - ahi;
45367             c = splitter * bcxtail;
45368             bhi = c - (c - bcxtail);
45369             blo = bcxtail - bhi;
45370             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45371             _i = s0 - t0;
45372             bvirt = s0 - _i;
45373             u[0] = s0 - (_i + bvirt) + (bvirt - t0);
45374             _j = s1 + _i;
45375             bvirt = _j - s1;
45376             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45377             _i = _0 - t1;
45378             bvirt = _0 - _i;
45379             u[1] = _0 - (_i + bvirt) + (bvirt - t1);
45380             u3 = _j + _i;
45381             bvirt = u3 - _j;
45382             u[2] = _j - (u3 - bvirt) + (_i - bvirt);
45383             u[3] = u3;
45384             const C2len = sum$1(C1len, C1, 4, u, C2);
45385
45386             s1 = acxtail * bcytail;
45387             c = splitter * acxtail;
45388             ahi = c - (c - acxtail);
45389             alo = acxtail - ahi;
45390             c = splitter * bcytail;
45391             bhi = c - (c - bcytail);
45392             blo = bcytail - bhi;
45393             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45394             t1 = acytail * bcxtail;
45395             c = splitter * acytail;
45396             ahi = c - (c - acytail);
45397             alo = acytail - ahi;
45398             c = splitter * bcxtail;
45399             bhi = c - (c - bcxtail);
45400             blo = bcxtail - bhi;
45401             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45402             _i = s0 - t0;
45403             bvirt = s0 - _i;
45404             u[0] = s0 - (_i + bvirt) + (bvirt - t0);
45405             _j = s1 + _i;
45406             bvirt = _j - s1;
45407             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45408             _i = _0 - t1;
45409             bvirt = _0 - _i;
45410             u[1] = _0 - (_i + bvirt) + (bvirt - t1);
45411             u3 = _j + _i;
45412             bvirt = u3 - _j;
45413             u[2] = _j - (u3 - bvirt) + (_i - bvirt);
45414             u[3] = u3;
45415             const Dlen = sum$1(C2len, C2, 4, u, D);
45416
45417             return D[Dlen - 1];
45418         }
45419
45420         function orient2d(ax, ay, bx, by, cx, cy) {
45421             const detleft = (ay - cy) * (bx - cx);
45422             const detright = (ax - cx) * (by - cy);
45423             const det = detleft - detright;
45424
45425             if (detleft === 0 || detright === 0 || (detleft > 0) !== (detright > 0)) return det;
45426
45427             const detsum = Math.abs(detleft + detright);
45428             if (Math.abs(det) >= ccwerrboundA * detsum) return det;
45429
45430             return -orient2dadapt(ax, ay, bx, by, cx, cy, detsum);
45431         }
45432
45433         /**
45434          * Signed area of the triangle (p0, p1, p2)
45435          * @param  {Array.<Number>} p0
45436          * @param  {Array.<Number>} p1
45437          * @param  {Array.<Number>} p2
45438          * @return {Number}
45439          */
45440         function signedArea(p0, p1, p2) {
45441           const res = orient2d(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]);
45442           if (res > 0) return -1;
45443           if (res < 0) return 1;
45444           return 0;
45445         }
45446
45447         /**
45448          * @param  {SweepEvent} e1
45449          * @param  {SweepEvent} e2
45450          * @return {Number}
45451          */
45452         function compareEvents(e1, e2) {
45453           const p1 = e1.point;
45454           const p2 = e2.point;
45455
45456           // Different x-coordinate
45457           if (p1[0] > p2[0]) return 1;
45458           if (p1[0] < p2[0]) return -1;
45459
45460           // Different points, but same x-coordinate
45461           // Event with lower y-coordinate is processed first
45462           if (p1[1] !== p2[1]) return p1[1] > p2[1] ? 1 : -1;
45463
45464           return specialCases(e1, e2, p1);
45465         }
45466
45467
45468         /* eslint-disable no-unused-vars */
45469         function specialCases(e1, e2, p1, p2) {
45470           // Same coordinates, but one is a left endpoint and the other is
45471           // a right endpoint. The right endpoint is processed first
45472           if (e1.left !== e2.left)
45473             return e1.left ? 1 : -1;
45474
45475           // const p2 = e1.otherEvent.point, p3 = e2.otherEvent.point;
45476           // const sa = (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1])
45477           // Same coordinates, both events
45478           // are left endpoints or right endpoints.
45479           // not collinear
45480           if (signedArea(p1, e1.otherEvent.point, e2.otherEvent.point) !== 0) {
45481             // the event associate to the bottom segment is processed first
45482             return (!e1.isBelow(e2.otherEvent.point)) ? 1 : -1;
45483           }
45484
45485           return (!e1.isSubject && e2.isSubject) ? 1 : -1;
45486         }
45487         /* eslint-enable no-unused-vars */
45488
45489         /**
45490          * @param  {SweepEvent} se
45491          * @param  {Array.<Number>} p
45492          * @param  {Queue} queue
45493          * @return {Queue}
45494          */
45495         function divideSegment(se, p, queue)  {
45496           const r = new SweepEvent(p, false, se,            se.isSubject);
45497           const l = new SweepEvent(p, true,  se.otherEvent, se.isSubject);
45498
45499           /* eslint-disable no-console */
45500           if (equals(se.point, se.otherEvent.point)) {
45501             console.warn('what is that, a collapsed segment?', se);
45502           }
45503           /* eslint-enable no-console */
45504
45505           r.contourId = l.contourId = se.contourId;
45506
45507           // avoid a rounding error. The left event would be processed after the right event
45508           if (compareEvents(l, se.otherEvent) > 0) {
45509             se.otherEvent.left = true;
45510             l.left = false;
45511           }
45512
45513           // avoid a rounding error. The left event would be processed after the right event
45514           // if (compareEvents(se, r) > 0) {}
45515
45516           se.otherEvent.otherEvent = l;
45517           se.otherEvent = r;
45518
45519           queue.push(l);
45520           queue.push(r);
45521
45522           return queue;
45523         }
45524
45525         //const EPS = 1e-9;
45526
45527         /**
45528          * Finds the magnitude of the cross product of two vectors (if we pretend
45529          * they're in three dimensions)
45530          *
45531          * @param {Object} a First vector
45532          * @param {Object} b Second vector
45533          * @private
45534          * @returns {Number} The magnitude of the cross product
45535          */
45536         function crossProduct(a, b) {
45537           return (a[0] * b[1]) - (a[1] * b[0]);
45538         }
45539
45540         /**
45541          * Finds the dot product of two vectors.
45542          *
45543          * @param {Object} a First vector
45544          * @param {Object} b Second vector
45545          * @private
45546          * @returns {Number} The dot product
45547          */
45548         function dotProduct(a, b) {
45549           return (a[0] * b[0]) + (a[1] * b[1]);
45550         }
45551
45552         /**
45553          * Finds the intersection (if any) between two line segments a and b, given the
45554          * line segments' end points a1, a2 and b1, b2.
45555          *
45556          * This algorithm is based on Schneider and Eberly.
45557          * http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf
45558          * Page 244.
45559          *
45560          * @param {Array.<Number>} a1 point of first line
45561          * @param {Array.<Number>} a2 point of first line
45562          * @param {Array.<Number>} b1 point of second line
45563          * @param {Array.<Number>} b2 point of second line
45564          * @param {Boolean=}       noEndpointTouch whether to skip single touchpoints
45565          *                                         (meaning connected segments) as
45566          *                                         intersections
45567          * @returns {Array.<Array.<Number>>|Null} If the lines intersect, the point of
45568          * intersection. If they overlap, the two end points of the overlapping segment.
45569          * Otherwise, null.
45570          */
45571         function intersection (a1, a2, b1, b2, noEndpointTouch) {
45572           // The algorithm expects our lines in the form P + sd, where P is a point,
45573           // s is on the interval [0, 1], and d is a vector.
45574           // We are passed two points. P can be the first point of each pair. The
45575           // vector, then, could be thought of as the distance (in x and y components)
45576           // from the first point to the second point.
45577           // So first, let's make our vectors:
45578           const va = [a2[0] - a1[0], a2[1] - a1[1]];
45579           const vb = [b2[0] - b1[0], b2[1] - b1[1]];
45580           // We also define a function to convert back to regular point form:
45581
45582           /* eslint-disable arrow-body-style */
45583
45584           function toPoint(p, s, d) {
45585             return [
45586               p[0] + s * d[0],
45587               p[1] + s * d[1]
45588             ];
45589           }
45590
45591           /* eslint-enable arrow-body-style */
45592
45593           // The rest is pretty much a straight port of the algorithm.
45594           const e = [b1[0] - a1[0], b1[1] - a1[1]];
45595           let kross    = crossProduct(va, vb);
45596           let sqrKross = kross * kross;
45597           const sqrLenA  = dotProduct(va, va);
45598           //const sqrLenB  = dotProduct(vb, vb);
45599
45600           // Check for line intersection. This works because of the properties of the
45601           // cross product -- specifically, two vectors are parallel if and only if the
45602           // cross product is the 0 vector. The full calculation involves relative error
45603           // to account for possible very small line segments. See Schneider & Eberly
45604           // for details.
45605           if (sqrKross > 0/* EPS * sqrLenB * sqLenA */) {
45606             // If they're not parallel, then (because these are line segments) they
45607             // still might not actually intersect. This code checks that the
45608             // intersection point of the lines is actually on both line segments.
45609             const s = crossProduct(e, vb) / kross;
45610             if (s < 0 || s > 1) {
45611               // not on line segment a
45612               return null;
45613             }
45614             const t = crossProduct(e, va) / kross;
45615             if (t < 0 || t > 1) {
45616               // not on line segment b
45617               return null;
45618             }
45619             if (s === 0 || s === 1) {
45620               // on an endpoint of line segment a
45621               return noEndpointTouch ? null : [toPoint(a1, s, va)];
45622             }
45623             if (t === 0 || t === 1) {
45624               // on an endpoint of line segment b
45625               return noEndpointTouch ? null : [toPoint(b1, t, vb)];
45626             }
45627             return [toPoint(a1, s, va)];
45628           }
45629
45630           // If we've reached this point, then the lines are either parallel or the
45631           // same, but the segments could overlap partially or fully, or not at all.
45632           // So we need to find the overlap, if any. To do that, we can use e, which is
45633           // the (vector) difference between the two initial points. If this is parallel
45634           // with the line itself, then the two lines are the same line, and there will
45635           // be overlap.
45636           //const sqrLenE = dotProduct(e, e);
45637           kross = crossProduct(e, va);
45638           sqrKross = kross * kross;
45639
45640           if (sqrKross > 0 /* EPS * sqLenB * sqLenE */) {
45641           // Lines are just parallel, not the same. No overlap.
45642             return null;
45643           }
45644
45645           const sa = dotProduct(va, e) / sqrLenA;
45646           const sb = sa + dotProduct(va, vb) / sqrLenA;
45647           const smin = Math.min(sa, sb);
45648           const smax = Math.max(sa, sb);
45649
45650           // this is, essentially, the FindIntersection acting on floats from
45651           // Schneider & Eberly, just inlined into this function.
45652           if (smin <= 1 && smax >= 0) {
45653
45654             // overlap on an end point
45655             if (smin === 1) {
45656               return noEndpointTouch ? null : [toPoint(a1, smin > 0 ? smin : 0, va)];
45657             }
45658
45659             if (smax === 0) {
45660               return noEndpointTouch ? null : [toPoint(a1, smax < 1 ? smax : 1, va)];
45661             }
45662
45663             if (noEndpointTouch && smin === 0 && smax === 1) return null;
45664
45665             // There's overlap on a segment -- two points of intersection. Return both.
45666             return [
45667               toPoint(a1, smin > 0 ? smin : 0, va),
45668               toPoint(a1, smax < 1 ? smax : 1, va)
45669             ];
45670           }
45671
45672           return null;
45673         }
45674
45675         /**
45676          * @param  {SweepEvent} se1
45677          * @param  {SweepEvent} se2
45678          * @param  {Queue}      queue
45679          * @return {Number}
45680          */
45681         function possibleIntersection (se1, se2, queue) {
45682           // that disallows self-intersecting polygons,
45683           // did cost us half a day, so I'll leave it
45684           // out of respect
45685           // if (se1.isSubject === se2.isSubject) return;
45686           const inter = intersection(
45687             se1.point, se1.otherEvent.point,
45688             se2.point, se2.otherEvent.point
45689           );
45690
45691           const nintersections = inter ? inter.length : 0;
45692           if (nintersections === 0) return 0; // no intersection
45693
45694           // the line segments intersect at an endpoint of both line segments
45695           if ((nintersections === 1) &&
45696               (equals(se1.point, se2.point) ||
45697                equals(se1.otherEvent.point, se2.otherEvent.point))) {
45698             return 0;
45699           }
45700
45701           if (nintersections === 2 && se1.isSubject === se2.isSubject) {
45702             // if(se1.contourId === se2.contourId){
45703             // console.warn('Edges of the same polygon overlap',
45704             //   se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point);
45705             // }
45706             //throw new Error('Edges of the same polygon overlap');
45707             return 0;
45708           }
45709
45710           // The line segments associated to se1 and se2 intersect
45711           if (nintersections === 1) {
45712
45713             // if the intersection point is not an endpoint of se1
45714             if (!equals(se1.point, inter[0]) && !equals(se1.otherEvent.point, inter[0])) {
45715               divideSegment(se1, inter[0], queue);
45716             }
45717
45718             // if the intersection point is not an endpoint of se2
45719             if (!equals(se2.point, inter[0]) && !equals(se2.otherEvent.point, inter[0])) {
45720               divideSegment(se2, inter[0], queue);
45721             }
45722             return 1;
45723           }
45724
45725           // The line segments associated to se1 and se2 overlap
45726           const events        = [];
45727           let leftCoincide  = false;
45728           let rightCoincide = false;
45729
45730           if (equals(se1.point, se2.point)) {
45731             leftCoincide = true; // linked
45732           } else if (compareEvents(se1, se2) === 1) {
45733             events.push(se2, se1);
45734           } else {
45735             events.push(se1, se2);
45736           }
45737
45738           if (equals(se1.otherEvent.point, se2.otherEvent.point)) {
45739             rightCoincide = true;
45740           } else if (compareEvents(se1.otherEvent, se2.otherEvent) === 1) {
45741             events.push(se2.otherEvent, se1.otherEvent);
45742           } else {
45743             events.push(se1.otherEvent, se2.otherEvent);
45744           }
45745
45746           if ((leftCoincide && rightCoincide) || leftCoincide) {
45747             // both line segments are equal or share the left endpoint
45748             se2.type = NON_CONTRIBUTING;
45749             se1.type = (se2.inOut === se1.inOut)
45750               ? SAME_TRANSITION : DIFFERENT_TRANSITION;
45751
45752             if (leftCoincide && !rightCoincide) {
45753               // honestly no idea, but changing events selection from [2, 1]
45754               // to [0, 1] fixes the overlapping self-intersecting polygons issue
45755               divideSegment(events[1].otherEvent, events[0].point, queue);
45756             }
45757             return 2;
45758           }
45759
45760           // the line segments share the right endpoint
45761           if (rightCoincide) {
45762             divideSegment(events[0], events[1].point, queue);
45763             return 3;
45764           }
45765
45766           // no line segment includes totally the other one
45767           if (events[0] !== events[3].otherEvent) {
45768             divideSegment(events[0], events[1].point, queue);
45769             divideSegment(events[1], events[2].point, queue);
45770             return 3;
45771           }
45772
45773           // one line segment includes the other one
45774           divideSegment(events[0], events[1].point, queue);
45775           divideSegment(events[3].otherEvent, events[2].point, queue);
45776
45777           return 3;
45778         }
45779
45780         /**
45781          * @param  {SweepEvent} le1
45782          * @param  {SweepEvent} le2
45783          * @return {Number}
45784          */
45785         function compareSegments(le1, le2) {
45786           if (le1 === le2) return 0;
45787
45788           // Segments are not collinear
45789           if (signedArea(le1.point, le1.otherEvent.point, le2.point) !== 0 ||
45790             signedArea(le1.point, le1.otherEvent.point, le2.otherEvent.point) !== 0) {
45791
45792             // If they share their left endpoint use the right endpoint to sort
45793             if (equals(le1.point, le2.point)) return le1.isBelow(le2.otherEvent.point) ? -1 : 1;
45794
45795             // Different left endpoint: use the left endpoint to sort
45796             if (le1.point[0] === le2.point[0]) return le1.point[1] < le2.point[1] ? -1 : 1;
45797
45798             // has the line segment associated to e1 been inserted
45799             // into S after the line segment associated to e2 ?
45800             if (compareEvents(le1, le2) === 1) return le2.isAbove(le1.point) ? -1 : 1;
45801
45802             // The line segment associated to e2 has been inserted
45803             // into S after the line segment associated to e1
45804             return le1.isBelow(le2.point) ? -1 : 1;
45805           }
45806
45807           if (le1.isSubject === le2.isSubject) { // same polygon
45808             let p1 = le1.point, p2 = le2.point;
45809             if (p1[0] === p2[0] && p1[1] === p2[1]/*equals(le1.point, le2.point)*/) {
45810               p1 = le1.otherEvent.point; p2 = le2.otherEvent.point;
45811               if (p1[0] === p2[0] && p1[1] === p2[1]) return 0;
45812               else return le1.contourId > le2.contourId ? 1 : -1;
45813             }
45814           } else { // Segments are collinear, but belong to separate polygons
45815             return le1.isSubject ? -1 : 1;
45816           }
45817
45818           return compareEvents(le1, le2) === 1 ? 1 : -1;
45819         }
45820
45821         function subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation) {
45822           const sweepLine = new SplayTree(compareSegments);
45823           const sortedEvents = [];
45824
45825           const rightbound = Math.min(sbbox[2], cbbox[2]);
45826
45827           let prev, next, begin;
45828
45829           while (eventQueue.length !== 0) {
45830             let event = eventQueue.pop();
45831             sortedEvents.push(event);
45832
45833             // optimization by bboxes for intersection and difference goes here
45834             if ((operation === INTERSECTION && event.point[0] > rightbound) ||
45835                 (operation === DIFFERENCE   && event.point[0] > sbbox[2])) {
45836               break;
45837             }
45838
45839             if (event.left) {
45840               next  = prev = sweepLine.insert(event);
45841               begin = sweepLine.minNode();
45842
45843               if (prev !== begin) prev = sweepLine.prev(prev);
45844               else                prev = null;
45845
45846               next = sweepLine.next(next);
45847
45848               const prevEvent = prev ? prev.key : null;
45849               let prevprevEvent;
45850               computeFields(event, prevEvent, operation);
45851               if (next) {
45852                 if (possibleIntersection(event, next.key, eventQueue) === 2) {
45853                   computeFields(event, prevEvent, operation);
45854                   computeFields(event, next.key, operation);
45855                 }
45856               }
45857
45858               if (prev) {
45859                 if (possibleIntersection(prev.key, event, eventQueue) === 2) {
45860                   let prevprev = prev;
45861                   if (prevprev !== begin) prevprev = sweepLine.prev(prevprev);
45862                   else                    prevprev = null;
45863
45864                   prevprevEvent = prevprev ? prevprev.key : null;
45865                   computeFields(prevEvent, prevprevEvent, operation);
45866                   computeFields(event,     prevEvent,     operation);
45867                 }
45868               }
45869             } else {
45870               event = event.otherEvent;
45871               next = prev = sweepLine.find(event);
45872
45873               if (prev && next) {
45874
45875                 if (prev !== begin) prev = sweepLine.prev(prev);
45876                 else                prev = null;
45877
45878                 next = sweepLine.next(next);
45879                 sweepLine.remove(event);
45880
45881                 if (next && prev) {
45882                   possibleIntersection(prev.key, next.key, eventQueue);
45883                 }
45884               }
45885             }
45886           }
45887           return sortedEvents;
45888         }
45889
45890         class Contour {
45891
45892           /**
45893            * Contour
45894            *
45895            * @class {Contour}
45896            */
45897           constructor() {
45898             this.points = [];
45899             this.holeIds = [];
45900             this.holeOf = null;
45901             this.depth = null;
45902           }
45903
45904           isExterior() {
45905             return this.holeOf == null;
45906           }
45907
45908         }
45909
45910         /**
45911          * @param  {Array.<SweepEvent>} sortedEvents
45912          * @return {Array.<SweepEvent>}
45913          */
45914         function orderEvents(sortedEvents) {
45915           let event, i, len, tmp;
45916           const resultEvents = [];
45917           for (i = 0, len = sortedEvents.length; i < len; i++) {
45918             event = sortedEvents[i];
45919             if ((event.left && event.inResult) ||
45920               (!event.left && event.otherEvent.inResult)) {
45921               resultEvents.push(event);
45922             }
45923           }
45924           // Due to overlapping edges the resultEvents array can be not wholly sorted
45925           let sorted = false;
45926           while (!sorted) {
45927             sorted = true;
45928             for (i = 0, len = resultEvents.length; i < len; i++) {
45929               if ((i + 1) < len &&
45930                 compareEvents(resultEvents[i], resultEvents[i + 1]) === 1) {
45931                 tmp = resultEvents[i];
45932                 resultEvents[i] = resultEvents[i + 1];
45933                 resultEvents[i + 1] = tmp;
45934                 sorted = false;
45935               }
45936             }
45937           }
45938
45939
45940           for (i = 0, len = resultEvents.length; i < len; i++) {
45941             event = resultEvents[i];
45942             event.otherPos = i;
45943           }
45944
45945           // imagine, the right event is found in the beginning of the queue,
45946           // when his left counterpart is not marked yet
45947           for (i = 0, len = resultEvents.length; i < len; i++) {
45948             event = resultEvents[i];
45949             if (!event.left) {
45950               tmp = event.otherPos;
45951               event.otherPos = event.otherEvent.otherPos;
45952               event.otherEvent.otherPos = tmp;
45953             }
45954           }
45955
45956           return resultEvents;
45957         }
45958
45959
45960         /**
45961          * @param  {Number} pos
45962          * @param  {Array.<SweepEvent>} resultEvents
45963          * @param  {Object>}    processed
45964          * @return {Number}
45965          */
45966         function nextPos(pos, resultEvents, processed, origPos) {
45967           let newPos = pos + 1,
45968               p = resultEvents[pos].point,
45969               p1;
45970           const length = resultEvents.length;
45971
45972           if (newPos < length)
45973             p1 = resultEvents[newPos].point;
45974
45975           while (newPos < length && p1[0] === p[0] && p1[1] === p[1]) {
45976             if (!processed[newPos]) {
45977               return newPos;
45978             } else   {
45979               newPos++;
45980             }
45981             p1 = resultEvents[newPos].point;
45982           }
45983
45984           newPos = pos - 1;
45985
45986           while (processed[newPos] && newPos > origPos) {
45987             newPos--;
45988           }
45989
45990           return newPos;
45991         }
45992
45993
45994         function initializeContourFromContext(event, contours, contourId) {
45995           const contour = new Contour();
45996           if (event.prevInResult != null) {
45997             const prevInResult = event.prevInResult;
45998             // Note that it is valid to query the "previous in result" for its output contour id,
45999             // because we must have already processed it (i.e., assigned an output contour id)
46000             // in an earlier iteration, otherwise it wouldn't be possible that it is "previous in
46001             // result".
46002             const lowerContourId = prevInResult.outputContourId;
46003             const lowerResultTransition = prevInResult.resultTransition;
46004             if (lowerResultTransition > 0) {
46005               // We are inside. Now we have to check if the thing below us is another hole or
46006               // an exterior contour.
46007               const lowerContour = contours[lowerContourId];
46008               if (lowerContour.holeOf != null) {
46009                 // The lower contour is a hole => Connect the new contour as a hole to its parent,
46010                 // and use same depth.
46011                 const parentContourId = lowerContour.holeOf;
46012                 contours[parentContourId].holeIds.push(contourId);
46013                 contour.holeOf = parentContourId;
46014                 contour.depth = contours[lowerContourId].depth;
46015               } else {
46016                 // The lower contour is an exterior contour => Connect the new contour as a hole,
46017                 // and increment depth.
46018                 contours[lowerContourId].holeIds.push(contourId);
46019                 contour.holeOf = lowerContourId;
46020                 contour.depth = contours[lowerContourId].depth + 1;
46021               }
46022             } else {
46023               // We are outside => this contour is an exterior contour of same depth.
46024               contour.holeOf = null;
46025               contour.depth = contours[lowerContourId].depth;
46026             }
46027           } else {
46028             // There is no lower/previous contour => this contour is an exterior contour of depth 0.
46029             contour.holeOf = null;
46030             contour.depth = 0;
46031           }
46032           return contour;
46033         }
46034
46035         /**
46036          * @param  {Array.<SweepEvent>} sortedEvents
46037          * @return {Array.<*>} polygons
46038          */
46039         function connectEdges(sortedEvents) {
46040           let i, len;
46041           const resultEvents = orderEvents(sortedEvents);
46042
46043           // "false"-filled array
46044           const processed = {};
46045           const contours = [];
46046
46047           for (i = 0, len = resultEvents.length; i < len; i++) {
46048
46049             if (processed[i]) {
46050               continue;
46051             }
46052
46053             const contourId = contours.length;
46054             const contour = initializeContourFromContext(resultEvents[i], contours, contourId);
46055
46056             // Helper function that combines marking an event as processed with assigning its output contour ID
46057             const markAsProcessed = (pos) => {
46058               processed[pos] = true;
46059               resultEvents[pos].outputContourId = contourId;
46060             };
46061
46062             let pos = i;
46063             let origPos = i;
46064
46065             const initial = resultEvents[i].point;
46066             contour.points.push(initial);
46067
46068             /* eslint no-constant-condition: "off" */
46069             while (true) {
46070               markAsProcessed(pos);
46071
46072               pos = resultEvents[pos].otherPos;
46073
46074               markAsProcessed(pos);
46075               contour.points.push(resultEvents[pos].point);
46076
46077               pos = nextPos(pos, resultEvents, processed, origPos);
46078
46079               if (pos == origPos) {
46080                 break;
46081               }
46082             }
46083
46084             contours.push(contour);
46085           }
46086
46087           return contours;
46088         }
46089
46090         var tinyqueue = TinyQueue;
46091         var _default$1 = TinyQueue;
46092
46093         function TinyQueue(data, compare) {
46094             if (!(this instanceof TinyQueue)) return new TinyQueue(data, compare);
46095
46096             this.data = data || [];
46097             this.length = this.data.length;
46098             this.compare = compare || defaultCompare$1;
46099
46100             if (this.length > 0) {
46101                 for (var i = (this.length >> 1) - 1; i >= 0; i--) this._down(i);
46102             }
46103         }
46104
46105         function defaultCompare$1(a, b) {
46106             return a < b ? -1 : a > b ? 1 : 0;
46107         }
46108
46109         TinyQueue.prototype = {
46110
46111             push: function (item) {
46112                 this.data.push(item);
46113                 this.length++;
46114                 this._up(this.length - 1);
46115             },
46116
46117             pop: function () {
46118                 if (this.length === 0) return undefined;
46119
46120                 var top = this.data[0];
46121                 this.length--;
46122
46123                 if (this.length > 0) {
46124                     this.data[0] = this.data[this.length];
46125                     this._down(0);
46126                 }
46127                 this.data.pop();
46128
46129                 return top;
46130             },
46131
46132             peek: function () {
46133                 return this.data[0];
46134             },
46135
46136             _up: function (pos) {
46137                 var data = this.data;
46138                 var compare = this.compare;
46139                 var item = data[pos];
46140
46141                 while (pos > 0) {
46142                     var parent = (pos - 1) >> 1;
46143                     var current = data[parent];
46144                     if (compare(item, current) >= 0) break;
46145                     data[pos] = current;
46146                     pos = parent;
46147                 }
46148
46149                 data[pos] = item;
46150             },
46151
46152             _down: function (pos) {
46153                 var data = this.data;
46154                 var compare = this.compare;
46155                 var halfLength = this.length >> 1;
46156                 var item = data[pos];
46157
46158                 while (pos < halfLength) {
46159                     var left = (pos << 1) + 1;
46160                     var right = left + 1;
46161                     var best = data[left];
46162
46163                     if (right < this.length && compare(data[right], best) < 0) {
46164                         left = right;
46165                         best = data[right];
46166                     }
46167                     if (compare(best, item) >= 0) break;
46168
46169                     data[pos] = best;
46170                     pos = left;
46171                 }
46172
46173                 data[pos] = item;
46174             }
46175         };
46176         tinyqueue.default = _default$1;
46177
46178         const max$2 = Math.max;
46179         const min = Math.min;
46180
46181         let contourId = 0;
46182
46183
46184         function processPolygon(contourOrHole, isSubject, depth, Q, bbox, isExteriorRing) {
46185           let i, len, s1, s2, e1, e2;
46186           for (i = 0, len = contourOrHole.length - 1; i < len; i++) {
46187             s1 = contourOrHole[i];
46188             s2 = contourOrHole[i + 1];
46189             e1 = new SweepEvent(s1, false, undefined, isSubject);
46190             e2 = new SweepEvent(s2, false, e1,        isSubject);
46191             e1.otherEvent = e2;
46192
46193             if (s1[0] === s2[0] && s1[1] === s2[1]) {
46194               continue; // skip collapsed edges, or it breaks
46195             }
46196
46197             e1.contourId = e2.contourId = depth;
46198             if (!isExteriorRing) {
46199               e1.isExteriorRing = false;
46200               e2.isExteriorRing = false;
46201             }
46202             if (compareEvents(e1, e2) > 0) {
46203               e2.left = true;
46204             } else {
46205               e1.left = true;
46206             }
46207
46208             const x = s1[0], y = s1[1];
46209             bbox[0] = min(bbox[0], x);
46210             bbox[1] = min(bbox[1], y);
46211             bbox[2] = max$2(bbox[2], x);
46212             bbox[3] = max$2(bbox[3], y);
46213
46214             // Pushing it so the queue is sorted from left to right,
46215             // with object on the left having the highest priority.
46216             Q.push(e1);
46217             Q.push(e2);
46218           }
46219         }
46220
46221
46222         function fillQueue(subject, clipping, sbbox, cbbox, operation) {
46223           const eventQueue = new tinyqueue(null, compareEvents);
46224           let polygonSet, isExteriorRing, i, ii, j, jj; //, k, kk;
46225
46226           for (i = 0, ii = subject.length; i < ii; i++) {
46227             polygonSet = subject[i];
46228             for (j = 0, jj = polygonSet.length; j < jj; j++) {
46229               isExteriorRing = j === 0;
46230               if (isExteriorRing) contourId++;
46231               processPolygon(polygonSet[j], true, contourId, eventQueue, sbbox, isExteriorRing);
46232             }
46233           }
46234
46235           for (i = 0, ii = clipping.length; i < ii; i++) {
46236             polygonSet = clipping[i];
46237             for (j = 0, jj = polygonSet.length; j < jj; j++) {
46238               isExteriorRing = j === 0;
46239               if (operation === DIFFERENCE) isExteriorRing = false;
46240               if (isExteriorRing) contourId++;
46241               processPolygon(polygonSet[j], false, contourId, eventQueue, cbbox, isExteriorRing);
46242             }
46243           }
46244
46245           return eventQueue;
46246         }
46247
46248         const EMPTY = [];
46249
46250
46251         function trivialOperation(subject, clipping, operation) {
46252           let result = null;
46253           if (subject.length * clipping.length === 0) {
46254             if        (operation === INTERSECTION) {
46255               result = EMPTY;
46256             } else if (operation === DIFFERENCE) {
46257               result = subject;
46258             } else if (operation === UNION ||
46259                        operation === XOR) {
46260               result = (subject.length === 0) ? clipping : subject;
46261             }
46262           }
46263           return result;
46264         }
46265
46266
46267         function compareBBoxes(subject, clipping, sbbox, cbbox, operation) {
46268           let result = null;
46269           if (sbbox[0] > cbbox[2] ||
46270               cbbox[0] > sbbox[2] ||
46271               sbbox[1] > cbbox[3] ||
46272               cbbox[1] > sbbox[3]) {
46273             if        (operation === INTERSECTION) {
46274               result = EMPTY;
46275             } else if (operation === DIFFERENCE) {
46276               result = subject;
46277             } else if (operation === UNION ||
46278                        operation === XOR) {
46279               result = subject.concat(clipping);
46280             }
46281           }
46282           return result;
46283         }
46284
46285
46286         function boolean(subject, clipping, operation) {
46287           if (typeof subject[0][0][0] === 'number') {
46288             subject = [subject];
46289           }
46290           if (typeof clipping[0][0][0] === 'number') {
46291             clipping = [clipping];
46292           }
46293           let trivial = trivialOperation(subject, clipping, operation);
46294           if (trivial) {
46295             return trivial === EMPTY ? null : trivial;
46296           }
46297           const sbbox = [Infinity, Infinity, -Infinity, -Infinity];
46298           const cbbox = [Infinity, Infinity, -Infinity, -Infinity];
46299
46300           // console.time('fill queue');
46301           const eventQueue = fillQueue(subject, clipping, sbbox, cbbox, operation);
46302           //console.timeEnd('fill queue');
46303
46304           trivial = compareBBoxes(subject, clipping, sbbox, cbbox, operation);
46305           if (trivial) {
46306             return trivial === EMPTY ? null : trivial;
46307           }
46308           // console.time('subdivide edges');
46309           const sortedEvents = subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation);
46310           //console.timeEnd('subdivide edges');
46311
46312           // console.time('connect vertices');
46313           const contours = connectEdges(sortedEvents);
46314           //console.timeEnd('connect vertices');
46315
46316           // Convert contours to polygons
46317           const polygons = [];
46318           for (let i = 0; i < contours.length; i++) {
46319             let contour = contours[i];
46320             if (contour.isExterior()) {
46321               // The exterior ring goes first
46322               let rings = [contour.points];
46323               // Followed by holes if any
46324               for (let j = 0; j < contour.holeIds.length; j++) {
46325                 let holeId = contour.holeIds[j];
46326                 rings.push(contours[holeId].points);
46327               }
46328               polygons.push(rings);
46329             }
46330           }
46331
46332           return polygons;
46333         }
46334
46335         function union (subject, clipping) {
46336           return boolean(subject, clipping, UNION);
46337         }
46338
46339         var read$6 = function (buffer, offset, isLE, mLen, nBytes) {
46340           var e, m;
46341           var eLen = (nBytes * 8) - mLen - 1;
46342           var eMax = (1 << eLen) - 1;
46343           var eBias = eMax >> 1;
46344           var nBits = -7;
46345           var i = isLE ? (nBytes - 1) : 0;
46346           var d = isLE ? -1 : 1;
46347           var s = buffer[offset + i];
46348
46349           i += d;
46350
46351           e = s & ((1 << (-nBits)) - 1);
46352           s >>= (-nBits);
46353           nBits += eLen;
46354           for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}
46355
46356           m = e & ((1 << (-nBits)) - 1);
46357           e >>= (-nBits);
46358           nBits += mLen;
46359           for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}
46360
46361           if (e === 0) {
46362             e = 1 - eBias;
46363           } else if (e === eMax) {
46364             return m ? NaN : ((s ? -1 : 1) * Infinity)
46365           } else {
46366             m = m + Math.pow(2, mLen);
46367             e = e - eBias;
46368           }
46369           return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
46370         };
46371
46372         var write$6 = function (buffer, value, offset, isLE, mLen, nBytes) {
46373           var e, m, c;
46374           var eLen = (nBytes * 8) - mLen - 1;
46375           var eMax = (1 << eLen) - 1;
46376           var eBias = eMax >> 1;
46377           var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0);
46378           var i = isLE ? 0 : (nBytes - 1);
46379           var d = isLE ? 1 : -1;
46380           var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
46381
46382           value = Math.abs(value);
46383
46384           if (isNaN(value) || value === Infinity) {
46385             m = isNaN(value) ? 1 : 0;
46386             e = eMax;
46387           } else {
46388             e = Math.floor(Math.log(value) / Math.LN2);
46389             if (value * (c = Math.pow(2, -e)) < 1) {
46390               e--;
46391               c *= 2;
46392             }
46393             if (e + eBias >= 1) {
46394               value += rt / c;
46395             } else {
46396               value += rt * Math.pow(2, 1 - eBias);
46397             }
46398             if (value * c >= 2) {
46399               e++;
46400               c /= 2;
46401             }
46402
46403             if (e + eBias >= eMax) {
46404               m = 0;
46405               e = eMax;
46406             } else if (e + eBias >= 1) {
46407               m = ((value * c) - 1) * Math.pow(2, mLen);
46408               e = e + eBias;
46409             } else {
46410               m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
46411               e = 0;
46412             }
46413           }
46414
46415           for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
46416
46417           e = (e << mLen) | m;
46418           eLen += mLen;
46419           for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
46420
46421           buffer[offset + i - d] |= s * 128;
46422         };
46423
46424         var ieee754 = {
46425                 read: read$6,
46426                 write: write$6
46427         };
46428
46429         var pbf = Pbf;
46430
46431
46432
46433         function Pbf(buf) {
46434             this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0);
46435             this.pos = 0;
46436             this.type = 0;
46437             this.length = this.buf.length;
46438         }
46439
46440         Pbf.Varint  = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum
46441         Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64
46442         Pbf.Bytes   = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields
46443         Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32
46444
46445         var SHIFT_LEFT_32 = (1 << 16) * (1 << 16),
46446             SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32;
46447
46448         // Threshold chosen based on both benchmarking and knowledge about browser string
46449         // data structures (which currently switch structure types at 12 bytes or more)
46450         var TEXT_DECODER_MIN_LENGTH = 12;
46451         var utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf8');
46452
46453         Pbf.prototype = {
46454
46455             destroy: function() {
46456                 this.buf = null;
46457             },
46458
46459             // === READING =================================================================
46460
46461             readFields: function(readField, result, end) {
46462                 end = end || this.length;
46463
46464                 while (this.pos < end) {
46465                     var val = this.readVarint(),
46466                         tag = val >> 3,
46467                         startPos = this.pos;
46468
46469                     this.type = val & 0x7;
46470                     readField(tag, result, this);
46471
46472                     if (this.pos === startPos) this.skip(val);
46473                 }
46474                 return result;
46475             },
46476
46477             readMessage: function(readField, result) {
46478                 return this.readFields(readField, result, this.readVarint() + this.pos);
46479             },
46480
46481             readFixed32: function() {
46482                 var val = readUInt32(this.buf, this.pos);
46483                 this.pos += 4;
46484                 return val;
46485             },
46486
46487             readSFixed32: function() {
46488                 var val = readInt32(this.buf, this.pos);
46489                 this.pos += 4;
46490                 return val;
46491             },
46492
46493             // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed)
46494
46495             readFixed64: function() {
46496                 var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
46497                 this.pos += 8;
46498                 return val;
46499             },
46500
46501             readSFixed64: function() {
46502                 var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
46503                 this.pos += 8;
46504                 return val;
46505             },
46506
46507             readFloat: function() {
46508                 var val = ieee754.read(this.buf, this.pos, true, 23, 4);
46509                 this.pos += 4;
46510                 return val;
46511             },
46512
46513             readDouble: function() {
46514                 var val = ieee754.read(this.buf, this.pos, true, 52, 8);
46515                 this.pos += 8;
46516                 return val;
46517             },
46518
46519             readVarint: function(isSigned) {
46520                 var buf = this.buf,
46521                     val, b;
46522
46523                 b = buf[this.pos++]; val  =  b & 0x7f;        if (b < 0x80) return val;
46524                 b = buf[this.pos++]; val |= (b & 0x7f) << 7;  if (b < 0x80) return val;
46525                 b = buf[this.pos++]; val |= (b & 0x7f) << 14; if (b < 0x80) return val;
46526                 b = buf[this.pos++]; val |= (b & 0x7f) << 21; if (b < 0x80) return val;
46527                 b = buf[this.pos];   val |= (b & 0x0f) << 28;
46528
46529                 return readVarintRemainder(val, isSigned, this);
46530             },
46531
46532             readVarint64: function() { // for compatibility with v2.0.1
46533                 return this.readVarint(true);
46534             },
46535
46536             readSVarint: function() {
46537                 var num = this.readVarint();
46538                 return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding
46539             },
46540
46541             readBoolean: function() {
46542                 return Boolean(this.readVarint());
46543             },
46544
46545             readString: function() {
46546                 var end = this.readVarint() + this.pos;
46547                 var pos = this.pos;
46548                 this.pos = end;
46549
46550                 if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) {
46551                     // longer strings are fast with the built-in browser TextDecoder API
46552                     return readUtf8TextDecoder(this.buf, pos, end);
46553                 }
46554                 // short strings are fast with our custom implementation
46555                 return readUtf8(this.buf, pos, end);
46556             },
46557
46558             readBytes: function() {
46559                 var end = this.readVarint() + this.pos,
46560                     buffer = this.buf.subarray(this.pos, end);
46561                 this.pos = end;
46562                 return buffer;
46563             },
46564
46565             // verbose for performance reasons; doesn't affect gzipped size
46566
46567             readPackedVarint: function(arr, isSigned) {
46568                 if (this.type !== Pbf.Bytes) return arr.push(this.readVarint(isSigned));
46569                 var end = readPackedEnd(this);
46570                 arr = arr || [];
46571                 while (this.pos < end) arr.push(this.readVarint(isSigned));
46572                 return arr;
46573             },
46574             readPackedSVarint: function(arr) {
46575                 if (this.type !== Pbf.Bytes) return arr.push(this.readSVarint());
46576                 var end = readPackedEnd(this);
46577                 arr = arr || [];
46578                 while (this.pos < end) arr.push(this.readSVarint());
46579                 return arr;
46580             },
46581             readPackedBoolean: function(arr) {
46582                 if (this.type !== Pbf.Bytes) return arr.push(this.readBoolean());
46583                 var end = readPackedEnd(this);
46584                 arr = arr || [];
46585                 while (this.pos < end) arr.push(this.readBoolean());
46586                 return arr;
46587             },
46588             readPackedFloat: function(arr) {
46589                 if (this.type !== Pbf.Bytes) return arr.push(this.readFloat());
46590                 var end = readPackedEnd(this);
46591                 arr = arr || [];
46592                 while (this.pos < end) arr.push(this.readFloat());
46593                 return arr;
46594             },
46595             readPackedDouble: function(arr) {
46596                 if (this.type !== Pbf.Bytes) return arr.push(this.readDouble());
46597                 var end = readPackedEnd(this);
46598                 arr = arr || [];
46599                 while (this.pos < end) arr.push(this.readDouble());
46600                 return arr;
46601             },
46602             readPackedFixed32: function(arr) {
46603                 if (this.type !== Pbf.Bytes) return arr.push(this.readFixed32());
46604                 var end = readPackedEnd(this);
46605                 arr = arr || [];
46606                 while (this.pos < end) arr.push(this.readFixed32());
46607                 return arr;
46608             },
46609             readPackedSFixed32: function(arr) {
46610                 if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed32());
46611                 var end = readPackedEnd(this);
46612                 arr = arr || [];
46613                 while (this.pos < end) arr.push(this.readSFixed32());
46614                 return arr;
46615             },
46616             readPackedFixed64: function(arr) {
46617                 if (this.type !== Pbf.Bytes) return arr.push(this.readFixed64());
46618                 var end = readPackedEnd(this);
46619                 arr = arr || [];
46620                 while (this.pos < end) arr.push(this.readFixed64());
46621                 return arr;
46622             },
46623             readPackedSFixed64: function(arr) {
46624                 if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed64());
46625                 var end = readPackedEnd(this);
46626                 arr = arr || [];
46627                 while (this.pos < end) arr.push(this.readSFixed64());
46628                 return arr;
46629             },
46630
46631             skip: function(val) {
46632                 var type = val & 0x7;
46633                 if (type === Pbf.Varint) while (this.buf[this.pos++] > 0x7f) {}
46634                 else if (type === Pbf.Bytes) this.pos = this.readVarint() + this.pos;
46635                 else if (type === Pbf.Fixed32) this.pos += 4;
46636                 else if (type === Pbf.Fixed64) this.pos += 8;
46637                 else throw new Error('Unimplemented type: ' + type);
46638             },
46639
46640             // === WRITING =================================================================
46641
46642             writeTag: function(tag, type) {
46643                 this.writeVarint((tag << 3) | type);
46644             },
46645
46646             realloc: function(min) {
46647                 var length = this.length || 16;
46648
46649                 while (length < this.pos + min) length *= 2;
46650
46651                 if (length !== this.length) {
46652                     var buf = new Uint8Array(length);
46653                     buf.set(this.buf);
46654                     this.buf = buf;
46655                     this.length = length;
46656                 }
46657             },
46658
46659             finish: function() {
46660                 this.length = this.pos;
46661                 this.pos = 0;
46662                 return this.buf.subarray(0, this.length);
46663             },
46664
46665             writeFixed32: function(val) {
46666                 this.realloc(4);
46667                 writeInt32(this.buf, val, this.pos);
46668                 this.pos += 4;
46669             },
46670
46671             writeSFixed32: function(val) {
46672                 this.realloc(4);
46673                 writeInt32(this.buf, val, this.pos);
46674                 this.pos += 4;
46675             },
46676
46677             writeFixed64: function(val) {
46678                 this.realloc(8);
46679                 writeInt32(this.buf, val & -1, this.pos);
46680                 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
46681                 this.pos += 8;
46682             },
46683
46684             writeSFixed64: function(val) {
46685                 this.realloc(8);
46686                 writeInt32(this.buf, val & -1, this.pos);
46687                 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
46688                 this.pos += 8;
46689             },
46690
46691             writeVarint: function(val) {
46692                 val = +val || 0;
46693
46694                 if (val > 0xfffffff || val < 0) {
46695                     writeBigVarint(val, this);
46696                     return;
46697                 }
46698
46699                 this.realloc(4);
46700
46701                 this.buf[this.pos++] =           val & 0x7f  | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return;
46702                 this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return;
46703                 this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return;
46704                 this.buf[this.pos++] =   (val >>> 7) & 0x7f;
46705             },
46706
46707             writeSVarint: function(val) {
46708                 this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2);
46709             },
46710
46711             writeBoolean: function(val) {
46712                 this.writeVarint(Boolean(val));
46713             },
46714
46715             writeString: function(str) {
46716                 str = String(str);
46717                 this.realloc(str.length * 4);
46718
46719                 this.pos++; // reserve 1 byte for short string length
46720
46721                 var startPos = this.pos;
46722                 // write the string directly to the buffer and see how much was written
46723                 this.pos = writeUtf8(this.buf, str, this.pos);
46724                 var len = this.pos - startPos;
46725
46726                 if (len >= 0x80) makeRoomForExtraLength(startPos, len, this);
46727
46728                 // finally, write the message length in the reserved place and restore the position
46729                 this.pos = startPos - 1;
46730                 this.writeVarint(len);
46731                 this.pos += len;
46732             },
46733
46734             writeFloat: function(val) {
46735                 this.realloc(4);
46736                 ieee754.write(this.buf, val, this.pos, true, 23, 4);
46737                 this.pos += 4;
46738             },
46739
46740             writeDouble: function(val) {
46741                 this.realloc(8);
46742                 ieee754.write(this.buf, val, this.pos, true, 52, 8);
46743                 this.pos += 8;
46744             },
46745
46746             writeBytes: function(buffer) {
46747                 var len = buffer.length;
46748                 this.writeVarint(len);
46749                 this.realloc(len);
46750                 for (var i = 0; i < len; i++) this.buf[this.pos++] = buffer[i];
46751             },
46752
46753             writeRawMessage: function(fn, obj) {
46754                 this.pos++; // reserve 1 byte for short message length
46755
46756                 // write the message directly to the buffer and see how much was written
46757                 var startPos = this.pos;
46758                 fn(obj, this);
46759                 var len = this.pos - startPos;
46760
46761                 if (len >= 0x80) makeRoomForExtraLength(startPos, len, this);
46762
46763                 // finally, write the message length in the reserved place and restore the position
46764                 this.pos = startPos - 1;
46765                 this.writeVarint(len);
46766                 this.pos += len;
46767             },
46768
46769             writeMessage: function(tag, fn, obj) {
46770                 this.writeTag(tag, Pbf.Bytes);
46771                 this.writeRawMessage(fn, obj);
46772             },
46773
46774             writePackedVarint:   function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedVarint, arr);   },
46775             writePackedSVarint:  function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSVarint, arr);  },
46776             writePackedBoolean:  function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedBoolean, arr);  },
46777             writePackedFloat:    function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFloat, arr);    },
46778             writePackedDouble:   function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedDouble, arr);   },
46779             writePackedFixed32:  function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFixed32, arr);  },
46780             writePackedSFixed32: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSFixed32, arr); },
46781             writePackedFixed64:  function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFixed64, arr);  },
46782             writePackedSFixed64: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSFixed64, arr); },
46783
46784             writeBytesField: function(tag, buffer) {
46785                 this.writeTag(tag, Pbf.Bytes);
46786                 this.writeBytes(buffer);
46787             },
46788             writeFixed32Field: function(tag, val) {
46789                 this.writeTag(tag, Pbf.Fixed32);
46790                 this.writeFixed32(val);
46791             },
46792             writeSFixed32Field: function(tag, val) {
46793                 this.writeTag(tag, Pbf.Fixed32);
46794                 this.writeSFixed32(val);
46795             },
46796             writeFixed64Field: function(tag, val) {
46797                 this.writeTag(tag, Pbf.Fixed64);
46798                 this.writeFixed64(val);
46799             },
46800             writeSFixed64Field: function(tag, val) {
46801                 this.writeTag(tag, Pbf.Fixed64);
46802                 this.writeSFixed64(val);
46803             },
46804             writeVarintField: function(tag, val) {
46805                 this.writeTag(tag, Pbf.Varint);
46806                 this.writeVarint(val);
46807             },
46808             writeSVarintField: function(tag, val) {
46809                 this.writeTag(tag, Pbf.Varint);
46810                 this.writeSVarint(val);
46811             },
46812             writeStringField: function(tag, str) {
46813                 this.writeTag(tag, Pbf.Bytes);
46814                 this.writeString(str);
46815             },
46816             writeFloatField: function(tag, val) {
46817                 this.writeTag(tag, Pbf.Fixed32);
46818                 this.writeFloat(val);
46819             },
46820             writeDoubleField: function(tag, val) {
46821                 this.writeTag(tag, Pbf.Fixed64);
46822                 this.writeDouble(val);
46823             },
46824             writeBooleanField: function(tag, val) {
46825                 this.writeVarintField(tag, Boolean(val));
46826             }
46827         };
46828
46829         function readVarintRemainder(l, s, p) {
46830             var buf = p.buf,
46831                 h, b;
46832
46833             b = buf[p.pos++]; h  = (b & 0x70) >> 4;  if (b < 0x80) return toNum(l, h, s);
46834             b = buf[p.pos++]; h |= (b & 0x7f) << 3;  if (b < 0x80) return toNum(l, h, s);
46835             b = buf[p.pos++]; h |= (b & 0x7f) << 10; if (b < 0x80) return toNum(l, h, s);
46836             b = buf[p.pos++]; h |= (b & 0x7f) << 17; if (b < 0x80) return toNum(l, h, s);
46837             b = buf[p.pos++]; h |= (b & 0x7f) << 24; if (b < 0x80) return toNum(l, h, s);
46838             b = buf[p.pos++]; h |= (b & 0x01) << 31; if (b < 0x80) return toNum(l, h, s);
46839
46840             throw new Error('Expected varint not more than 10 bytes');
46841         }
46842
46843         function readPackedEnd(pbf) {
46844             return pbf.type === Pbf.Bytes ?
46845                 pbf.readVarint() + pbf.pos : pbf.pos + 1;
46846         }
46847
46848         function toNum(low, high, isSigned) {
46849             if (isSigned) {
46850                 return high * 0x100000000 + (low >>> 0);
46851             }
46852
46853             return ((high >>> 0) * 0x100000000) + (low >>> 0);
46854         }
46855
46856         function writeBigVarint(val, pbf) {
46857             var low, high;
46858
46859             if (val >= 0) {
46860                 low  = (val % 0x100000000) | 0;
46861                 high = (val / 0x100000000) | 0;
46862             } else {
46863                 low  = ~(-val % 0x100000000);
46864                 high = ~(-val / 0x100000000);
46865
46866                 if (low ^ 0xffffffff) {
46867                     low = (low + 1) | 0;
46868                 } else {
46869                     low = 0;
46870                     high = (high + 1) | 0;
46871                 }
46872             }
46873
46874             if (val >= 0x10000000000000000 || val < -0x10000000000000000) {
46875                 throw new Error('Given varint doesn\'t fit into 10 bytes');
46876             }
46877
46878             pbf.realloc(10);
46879
46880             writeBigVarintLow(low, high, pbf);
46881             writeBigVarintHigh(high, pbf);
46882         }
46883
46884         function writeBigVarintLow(low, high, pbf) {
46885             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
46886             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
46887             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
46888             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
46889             pbf.buf[pbf.pos]   = low & 0x7f;
46890         }
46891
46892         function writeBigVarintHigh(high, pbf) {
46893             var lsb = (high & 0x07) << 4;
46894
46895             pbf.buf[pbf.pos++] |= lsb         | ((high >>>= 3) ? 0x80 : 0); if (!high) return;
46896             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;
46897             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;
46898             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;
46899             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;
46900             pbf.buf[pbf.pos++]  = high & 0x7f;
46901         }
46902
46903         function makeRoomForExtraLength(startPos, len, pbf) {
46904             var extraLen =
46905                 len <= 0x3fff ? 1 :
46906                 len <= 0x1fffff ? 2 :
46907                 len <= 0xfffffff ? 3 : Math.floor(Math.log(len) / (Math.LN2 * 7));
46908
46909             // if 1 byte isn't enough for encoding message length, shift the data to the right
46910             pbf.realloc(extraLen);
46911             for (var i = pbf.pos - 1; i >= startPos; i--) pbf.buf[i + extraLen] = pbf.buf[i];
46912         }
46913
46914         function writePackedVarint(arr, pbf)   { for (var i = 0; i < arr.length; i++) pbf.writeVarint(arr[i]);   }
46915         function writePackedSVarint(arr, pbf)  { for (var i = 0; i < arr.length; i++) pbf.writeSVarint(arr[i]);  }
46916         function writePackedFloat(arr, pbf)    { for (var i = 0; i < arr.length; i++) pbf.writeFloat(arr[i]);    }
46917         function writePackedDouble(arr, pbf)   { for (var i = 0; i < arr.length; i++) pbf.writeDouble(arr[i]);   }
46918         function writePackedBoolean(arr, pbf)  { for (var i = 0; i < arr.length; i++) pbf.writeBoolean(arr[i]);  }
46919         function writePackedFixed32(arr, pbf)  { for (var i = 0; i < arr.length; i++) pbf.writeFixed32(arr[i]);  }
46920         function writePackedSFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed32(arr[i]); }
46921         function writePackedFixed64(arr, pbf)  { for (var i = 0; i < arr.length; i++) pbf.writeFixed64(arr[i]);  }
46922         function writePackedSFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed64(arr[i]); }
46923
46924         // Buffer code below from https://github.com/feross/buffer, MIT-licensed
46925
46926         function readUInt32(buf, pos) {
46927             return ((buf[pos]) |
46928                 (buf[pos + 1] << 8) |
46929                 (buf[pos + 2] << 16)) +
46930                 (buf[pos + 3] * 0x1000000);
46931         }
46932
46933         function writeInt32(buf, val, pos) {
46934             buf[pos] = val;
46935             buf[pos + 1] = (val >>> 8);
46936             buf[pos + 2] = (val >>> 16);
46937             buf[pos + 3] = (val >>> 24);
46938         }
46939
46940         function readInt32(buf, pos) {
46941             return ((buf[pos]) |
46942                 (buf[pos + 1] << 8) |
46943                 (buf[pos + 2] << 16)) +
46944                 (buf[pos + 3] << 24);
46945         }
46946
46947         function readUtf8(buf, pos, end) {
46948             var str = '';
46949             var i = pos;
46950
46951             while (i < end) {
46952                 var b0 = buf[i];
46953                 var c = null; // codepoint
46954                 var bytesPerSequence =
46955                     b0 > 0xEF ? 4 :
46956                     b0 > 0xDF ? 3 :
46957                     b0 > 0xBF ? 2 : 1;
46958
46959                 if (i + bytesPerSequence > end) break;
46960
46961                 var b1, b2, b3;
46962
46963                 if (bytesPerSequence === 1) {
46964                     if (b0 < 0x80) {
46965                         c = b0;
46966                     }
46967                 } else if (bytesPerSequence === 2) {
46968                     b1 = buf[i + 1];
46969                     if ((b1 & 0xC0) === 0x80) {
46970                         c = (b0 & 0x1F) << 0x6 | (b1 & 0x3F);
46971                         if (c <= 0x7F) {
46972                             c = null;
46973                         }
46974                     }
46975                 } else if (bytesPerSequence === 3) {
46976                     b1 = buf[i + 1];
46977                     b2 = buf[i + 2];
46978                     if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) {
46979                         c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | (b2 & 0x3F);
46980                         if (c <= 0x7FF || (c >= 0xD800 && c <= 0xDFFF)) {
46981                             c = null;
46982                         }
46983                     }
46984                 } else if (bytesPerSequence === 4) {
46985                     b1 = buf[i + 1];
46986                     b2 = buf[i + 2];
46987                     b3 = buf[i + 3];
46988                     if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {
46989                         c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | (b3 & 0x3F);
46990                         if (c <= 0xFFFF || c >= 0x110000) {
46991                             c = null;
46992                         }
46993                     }
46994                 }
46995
46996                 if (c === null) {
46997                     c = 0xFFFD;
46998                     bytesPerSequence = 1;
46999
47000                 } else if (c > 0xFFFF) {
47001                     c -= 0x10000;
47002                     str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800);
47003                     c = 0xDC00 | c & 0x3FF;
47004                 }
47005
47006                 str += String.fromCharCode(c);
47007                 i += bytesPerSequence;
47008             }
47009
47010             return str;
47011         }
47012
47013         function readUtf8TextDecoder(buf, pos, end) {
47014             return utf8TextDecoder.decode(buf.subarray(pos, end));
47015         }
47016
47017         function writeUtf8(buf, str, pos) {
47018             for (var i = 0, c, lead; i < str.length; i++) {
47019                 c = str.charCodeAt(i); // code point
47020
47021                 if (c > 0xD7FF && c < 0xE000) {
47022                     if (lead) {
47023                         if (c < 0xDC00) {
47024                             buf[pos++] = 0xEF;
47025                             buf[pos++] = 0xBF;
47026                             buf[pos++] = 0xBD;
47027                             lead = c;
47028                             continue;
47029                         } else {
47030                             c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000;
47031                             lead = null;
47032                         }
47033                     } else {
47034                         if (c > 0xDBFF || (i + 1 === str.length)) {
47035                             buf[pos++] = 0xEF;
47036                             buf[pos++] = 0xBF;
47037                             buf[pos++] = 0xBD;
47038                         } else {
47039                             lead = c;
47040                         }
47041                         continue;
47042                     }
47043                 } else if (lead) {
47044                     buf[pos++] = 0xEF;
47045                     buf[pos++] = 0xBF;
47046                     buf[pos++] = 0xBD;
47047                     lead = null;
47048                 }
47049
47050                 if (c < 0x80) {
47051                     buf[pos++] = c;
47052                 } else {
47053                     if (c < 0x800) {
47054                         buf[pos++] = c >> 0x6 | 0xC0;
47055                     } else {
47056                         if (c < 0x10000) {
47057                             buf[pos++] = c >> 0xC | 0xE0;
47058                         } else {
47059                             buf[pos++] = c >> 0x12 | 0xF0;
47060                             buf[pos++] = c >> 0xC & 0x3F | 0x80;
47061                         }
47062                         buf[pos++] = c >> 0x6 & 0x3F | 0x80;
47063                     }
47064                     buf[pos++] = c & 0x3F | 0x80;
47065                 }
47066             }
47067             return pos;
47068         }
47069
47070         var pointGeometry = Point;
47071
47072         /**
47073          * A standalone point geometry with useful accessor, comparison, and
47074          * modification methods.
47075          *
47076          * @class Point
47077          * @param {Number} x the x-coordinate. this could be longitude or screen
47078          * pixels, or any other sort of unit.
47079          * @param {Number} y the y-coordinate. this could be latitude or screen
47080          * pixels, or any other sort of unit.
47081          * @example
47082          * var point = new Point(-77, 38);
47083          */
47084         function Point(x, y) {
47085             this.x = x;
47086             this.y = y;
47087         }
47088
47089         Point.prototype = {
47090
47091             /**
47092              * Clone this point, returning a new point that can be modified
47093              * without affecting the old one.
47094              * @return {Point} the clone
47095              */
47096             clone: function() { return new Point(this.x, this.y); },
47097
47098             /**
47099              * Add this point's x & y coordinates to another point,
47100              * yielding a new point.
47101              * @param {Point} p the other point
47102              * @return {Point} output point
47103              */
47104             add:     function(p) { return this.clone()._add(p); },
47105
47106             /**
47107              * Subtract this point's x & y coordinates to from point,
47108              * yielding a new point.
47109              * @param {Point} p the other point
47110              * @return {Point} output point
47111              */
47112             sub:     function(p) { return this.clone()._sub(p); },
47113
47114             /**
47115              * Multiply 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             multByPoint:    function(p) { return this.clone()._multByPoint(p); },
47121
47122             /**
47123              * Divide this point's x & y coordinates by point,
47124              * yielding a new point.
47125              * @param {Point} p the other point
47126              * @return {Point} output point
47127              */
47128             divByPoint:     function(p) { return this.clone()._divByPoint(p); },
47129
47130             /**
47131              * Multiply 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             mult:    function(k) { return this.clone()._mult(k); },
47137
47138             /**
47139              * Divide this point's x & y coordinates by a factor,
47140              * yielding a new point.
47141              * @param {Point} k factor
47142              * @return {Point} output point
47143              */
47144             div:     function(k) { return this.clone()._div(k); },
47145
47146             /**
47147              * Rotate this point around the 0, 0 origin by an angle a,
47148              * given in radians
47149              * @param {Number} a angle to rotate around, in radians
47150              * @return {Point} output point
47151              */
47152             rotate:  function(a) { return this.clone()._rotate(a); },
47153
47154             /**
47155              * Rotate this point around p point by an angle a,
47156              * given in radians
47157              * @param {Number} a angle to rotate around, in radians
47158              * @param {Point} p Point to rotate around
47159              * @return {Point} output point
47160              */
47161             rotateAround:  function(a,p) { return this.clone()._rotateAround(a,p); },
47162
47163             /**
47164              * Multiply this point by a 4x1 transformation matrix
47165              * @param {Array<Number>} m transformation matrix
47166              * @return {Point} output point
47167              */
47168             matMult: function(m) { return this.clone()._matMult(m); },
47169
47170             /**
47171              * Calculate this point but as a unit vector from 0, 0, meaning
47172              * that the distance from the resulting point to the 0, 0
47173              * coordinate will be equal to 1 and the angle from the resulting
47174              * point to the 0, 0 coordinate will be the same as before.
47175              * @return {Point} unit vector point
47176              */
47177             unit:    function() { return this.clone()._unit(); },
47178
47179             /**
47180              * Compute a perpendicular point, where the new y coordinate
47181              * is the old x coordinate and the new x coordinate is the old y
47182              * coordinate multiplied by -1
47183              * @return {Point} perpendicular point
47184              */
47185             perp:    function() { return this.clone()._perp(); },
47186
47187             /**
47188              * Return a version of this point with the x & y coordinates
47189              * rounded to integers.
47190              * @return {Point} rounded point
47191              */
47192             round:   function() { return this.clone()._round(); },
47193
47194             /**
47195              * Return the magitude of this point: this is the Euclidean
47196              * distance from the 0, 0 coordinate to this point's x and y
47197              * coordinates.
47198              * @return {Number} magnitude
47199              */
47200             mag: function() {
47201                 return Math.sqrt(this.x * this.x + this.y * this.y);
47202             },
47203
47204             /**
47205              * Judge whether this point is equal to another point, returning
47206              * true or false.
47207              * @param {Point} other the other point
47208              * @return {boolean} whether the points are equal
47209              */
47210             equals: function(other) {
47211                 return this.x === other.x &&
47212                        this.y === other.y;
47213             },
47214
47215             /**
47216              * Calculate the distance from this point to another point
47217              * @param {Point} p the other point
47218              * @return {Number} distance
47219              */
47220             dist: function(p) {
47221                 return Math.sqrt(this.distSqr(p));
47222             },
47223
47224             /**
47225              * Calculate the distance from this point to another point,
47226              * without the square root step. Useful if you're comparing
47227              * relative distances.
47228              * @param {Point} p the other point
47229              * @return {Number} distance
47230              */
47231             distSqr: function(p) {
47232                 var dx = p.x - this.x,
47233                     dy = p.y - this.y;
47234                 return dx * dx + dy * dy;
47235             },
47236
47237             /**
47238              * Get the angle from the 0, 0 coordinate to this point, in radians
47239              * coordinates.
47240              * @return {Number} angle
47241              */
47242             angle: function() {
47243                 return Math.atan2(this.y, this.x);
47244             },
47245
47246             /**
47247              * Get the angle from this point to another point, in radians
47248              * @param {Point} b the other point
47249              * @return {Number} angle
47250              */
47251             angleTo: function(b) {
47252                 return Math.atan2(this.y - b.y, this.x - b.x);
47253             },
47254
47255             /**
47256              * Get the angle between this point and another point, in radians
47257              * @param {Point} b the other point
47258              * @return {Number} angle
47259              */
47260             angleWith: function(b) {
47261                 return this.angleWithSep(b.x, b.y);
47262             },
47263
47264             /*
47265              * Find the angle of the two vectors, solving the formula for
47266              * the cross product a x b = |a||b|sin(θ) for θ.
47267              * @param {Number} x the x-coordinate
47268              * @param {Number} y the y-coordinate
47269              * @return {Number} the angle in radians
47270              */
47271             angleWithSep: function(x, y) {
47272                 return Math.atan2(
47273                     this.x * y - this.y * x,
47274                     this.x * x + this.y * y);
47275             },
47276
47277             _matMult: function(m) {
47278                 var x = m[0] * this.x + m[1] * this.y,
47279                     y = m[2] * this.x + m[3] * this.y;
47280                 this.x = x;
47281                 this.y = y;
47282                 return this;
47283             },
47284
47285             _add: function(p) {
47286                 this.x += p.x;
47287                 this.y += p.y;
47288                 return this;
47289             },
47290
47291             _sub: function(p) {
47292                 this.x -= p.x;
47293                 this.y -= p.y;
47294                 return this;
47295             },
47296
47297             _mult: function(k) {
47298                 this.x *= k;
47299                 this.y *= k;
47300                 return this;
47301             },
47302
47303             _div: function(k) {
47304                 this.x /= k;
47305                 this.y /= k;
47306                 return this;
47307             },
47308
47309             _multByPoint: function(p) {
47310                 this.x *= p.x;
47311                 this.y *= p.y;
47312                 return this;
47313             },
47314
47315             _divByPoint: function(p) {
47316                 this.x /= p.x;
47317                 this.y /= p.y;
47318                 return this;
47319             },
47320
47321             _unit: function() {
47322                 this._div(this.mag());
47323                 return this;
47324             },
47325
47326             _perp: function() {
47327                 var y = this.y;
47328                 this.y = this.x;
47329                 this.x = -y;
47330                 return this;
47331             },
47332
47333             _rotate: function(angle) {
47334                 var cos = Math.cos(angle),
47335                     sin = Math.sin(angle),
47336                     x = cos * this.x - sin * this.y,
47337                     y = sin * this.x + cos * this.y;
47338                 this.x = x;
47339                 this.y = y;
47340                 return this;
47341             },
47342
47343             _rotateAround: function(angle, p) {
47344                 var cos = Math.cos(angle),
47345                     sin = Math.sin(angle),
47346                     x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y),
47347                     y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y);
47348                 this.x = x;
47349                 this.y = y;
47350                 return this;
47351             },
47352
47353             _round: function() {
47354                 this.x = Math.round(this.x);
47355                 this.y = Math.round(this.y);
47356                 return this;
47357             }
47358         };
47359
47360         /**
47361          * Construct a point from an array if necessary, otherwise if the input
47362          * is already a Point, or an unknown type, return it unchanged
47363          * @param {Array<Number>|Point|*} a any kind of input value
47364          * @return {Point} constructed point, or passed-through value.
47365          * @example
47366          * // this
47367          * var point = Point.convert([0, 1]);
47368          * // is equivalent to
47369          * var point = new Point(0, 1);
47370          */
47371         Point.convert = function (a) {
47372             if (a instanceof Point) {
47373                 return a;
47374             }
47375             if (Array.isArray(a)) {
47376                 return new Point(a[0], a[1]);
47377             }
47378             return a;
47379         };
47380
47381         var vectortilefeature = VectorTileFeature;
47382
47383         function VectorTileFeature(pbf, end, extent, keys, values) {
47384             // Public
47385             this.properties = {};
47386             this.extent = extent;
47387             this.type = 0;
47388
47389             // Private
47390             this._pbf = pbf;
47391             this._geometry = -1;
47392             this._keys = keys;
47393             this._values = values;
47394
47395             pbf.readFields(readFeature, this, end);
47396         }
47397
47398         function readFeature(tag, feature, pbf) {
47399             if (tag == 1) feature.id = pbf.readVarint();
47400             else if (tag == 2) readTag(pbf, feature);
47401             else if (tag == 3) feature.type = pbf.readVarint();
47402             else if (tag == 4) feature._geometry = pbf.pos;
47403         }
47404
47405         function readTag(pbf, feature) {
47406             var end = pbf.readVarint() + pbf.pos;
47407
47408             while (pbf.pos < end) {
47409                 var key = feature._keys[pbf.readVarint()],
47410                     value = feature._values[pbf.readVarint()];
47411                 feature.properties[key] = value;
47412             }
47413         }
47414
47415         VectorTileFeature.types = ['Unknown', 'Point', 'LineString', 'Polygon'];
47416
47417         VectorTileFeature.prototype.loadGeometry = function() {
47418             var pbf = this._pbf;
47419             pbf.pos = this._geometry;
47420
47421             var end = pbf.readVarint() + pbf.pos,
47422                 cmd = 1,
47423                 length = 0,
47424                 x = 0,
47425                 y = 0,
47426                 lines = [],
47427                 line;
47428
47429             while (pbf.pos < end) {
47430                 if (length <= 0) {
47431                     var cmdLen = pbf.readVarint();
47432                     cmd = cmdLen & 0x7;
47433                     length = cmdLen >> 3;
47434                 }
47435
47436                 length--;
47437
47438                 if (cmd === 1 || cmd === 2) {
47439                     x += pbf.readSVarint();
47440                     y += pbf.readSVarint();
47441
47442                     if (cmd === 1) { // moveTo
47443                         if (line) lines.push(line);
47444                         line = [];
47445                     }
47446
47447                     line.push(new pointGeometry(x, y));
47448
47449                 } else if (cmd === 7) {
47450
47451                     // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90
47452                     if (line) {
47453                         line.push(line[0].clone()); // closePolygon
47454                     }
47455
47456                 } else {
47457                     throw new Error('unknown command ' + cmd);
47458                 }
47459             }
47460
47461             if (line) lines.push(line);
47462
47463             return lines;
47464         };
47465
47466         VectorTileFeature.prototype.bbox = function() {
47467             var pbf = this._pbf;
47468             pbf.pos = this._geometry;
47469
47470             var end = pbf.readVarint() + pbf.pos,
47471                 cmd = 1,
47472                 length = 0,
47473                 x = 0,
47474                 y = 0,
47475                 x1 = Infinity,
47476                 x2 = -Infinity,
47477                 y1 = Infinity,
47478                 y2 = -Infinity;
47479
47480             while (pbf.pos < end) {
47481                 if (length <= 0) {
47482                     var cmdLen = pbf.readVarint();
47483                     cmd = cmdLen & 0x7;
47484                     length = cmdLen >> 3;
47485                 }
47486
47487                 length--;
47488
47489                 if (cmd === 1 || cmd === 2) {
47490                     x += pbf.readSVarint();
47491                     y += pbf.readSVarint();
47492                     if (x < x1) x1 = x;
47493                     if (x > x2) x2 = x;
47494                     if (y < y1) y1 = y;
47495                     if (y > y2) y2 = y;
47496
47497                 } else if (cmd !== 7) {
47498                     throw new Error('unknown command ' + cmd);
47499                 }
47500             }
47501
47502             return [x1, y1, x2, y2];
47503         };
47504
47505         VectorTileFeature.prototype.toGeoJSON = function(x, y, z) {
47506             var size = this.extent * Math.pow(2, z),
47507                 x0 = this.extent * x,
47508                 y0 = this.extent * y,
47509                 coords = this.loadGeometry(),
47510                 type = VectorTileFeature.types[this.type],
47511                 i, j;
47512
47513             function project(line) {
47514                 for (var j = 0; j < line.length; j++) {
47515                     var p = line[j], y2 = 180 - (p.y + y0) * 360 / size;
47516                     line[j] = [
47517                         (p.x + x0) * 360 / size - 180,
47518                         360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90
47519                     ];
47520                 }
47521             }
47522
47523             switch (this.type) {
47524             case 1:
47525                 var points = [];
47526                 for (i = 0; i < coords.length; i++) {
47527                     points[i] = coords[i][0];
47528                 }
47529                 coords = points;
47530                 project(coords);
47531                 break;
47532
47533             case 2:
47534                 for (i = 0; i < coords.length; i++) {
47535                     project(coords[i]);
47536                 }
47537                 break;
47538
47539             case 3:
47540                 coords = classifyRings(coords);
47541                 for (i = 0; i < coords.length; i++) {
47542                     for (j = 0; j < coords[i].length; j++) {
47543                         project(coords[i][j]);
47544                     }
47545                 }
47546                 break;
47547             }
47548
47549             if (coords.length === 1) {
47550                 coords = coords[0];
47551             } else {
47552                 type = 'Multi' + type;
47553             }
47554
47555             var result = {
47556                 type: "Feature",
47557                 geometry: {
47558                     type: type,
47559                     coordinates: coords
47560                 },
47561                 properties: this.properties
47562             };
47563
47564             if ('id' in this) {
47565                 result.id = this.id;
47566             }
47567
47568             return result;
47569         };
47570
47571         // classifies an array of rings into polygons with outer rings and holes
47572
47573         function classifyRings(rings) {
47574             var len = rings.length;
47575
47576             if (len <= 1) return [rings];
47577
47578             var polygons = [],
47579                 polygon,
47580                 ccw;
47581
47582             for (var i = 0; i < len; i++) {
47583                 var area = signedArea$1(rings[i]);
47584                 if (area === 0) continue;
47585
47586                 if (ccw === undefined) ccw = area < 0;
47587
47588                 if (ccw === area < 0) {
47589                     if (polygon) polygons.push(polygon);
47590                     polygon = [rings[i]];
47591
47592                 } else {
47593                     polygon.push(rings[i]);
47594                 }
47595             }
47596             if (polygon) polygons.push(polygon);
47597
47598             return polygons;
47599         }
47600
47601         function signedArea$1(ring) {
47602             var sum = 0;
47603             for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
47604                 p1 = ring[i];
47605                 p2 = ring[j];
47606                 sum += (p2.x - p1.x) * (p1.y + p2.y);
47607             }
47608             return sum;
47609         }
47610
47611         var vectortilelayer = VectorTileLayer;
47612
47613         function VectorTileLayer(pbf, end) {
47614             // Public
47615             this.version = 1;
47616             this.name = null;
47617             this.extent = 4096;
47618             this.length = 0;
47619
47620             // Private
47621             this._pbf = pbf;
47622             this._keys = [];
47623             this._values = [];
47624             this._features = [];
47625
47626             pbf.readFields(readLayer, this, end);
47627
47628             this.length = this._features.length;
47629         }
47630
47631         function readLayer(tag, layer, pbf) {
47632             if (tag === 15) layer.version = pbf.readVarint();
47633             else if (tag === 1) layer.name = pbf.readString();
47634             else if (tag === 5) layer.extent = pbf.readVarint();
47635             else if (tag === 2) layer._features.push(pbf.pos);
47636             else if (tag === 3) layer._keys.push(pbf.readString());
47637             else if (tag === 4) layer._values.push(readValueMessage(pbf));
47638         }
47639
47640         function readValueMessage(pbf) {
47641             var value = null,
47642                 end = pbf.readVarint() + pbf.pos;
47643
47644             while (pbf.pos < end) {
47645                 var tag = pbf.readVarint() >> 3;
47646
47647                 value = tag === 1 ? pbf.readString() :
47648                     tag === 2 ? pbf.readFloat() :
47649                     tag === 3 ? pbf.readDouble() :
47650                     tag === 4 ? pbf.readVarint64() :
47651                     tag === 5 ? pbf.readVarint() :
47652                     tag === 6 ? pbf.readSVarint() :
47653                     tag === 7 ? pbf.readBoolean() : null;
47654             }
47655
47656             return value;
47657         }
47658
47659         // return feature `i` from this layer as a `VectorTileFeature`
47660         VectorTileLayer.prototype.feature = function(i) {
47661             if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds');
47662
47663             this._pbf.pos = this._features[i];
47664
47665             var end = this._pbf.readVarint() + this._pbf.pos;
47666             return new vectortilefeature(this._pbf, end, this.extent, this._keys, this._values);
47667         };
47668
47669         var vectortile = VectorTile;
47670
47671         function VectorTile(pbf, end) {
47672             this.layers = pbf.readFields(readTile, {}, end);
47673         }
47674
47675         function readTile(tag, layers, pbf) {
47676             if (tag === 3) {
47677                 var layer = new vectortilelayer(pbf, pbf.readVarint() + pbf.pos);
47678                 if (layer.length) layers[layer.name] = layer;
47679             }
47680         }
47681
47682         var VectorTile$1 = vectortile;
47683         var VectorTileFeature$1 = vectortilefeature;
47684         var VectorTileLayer$1 = vectortilelayer;
47685
47686         var vectorTile = {
47687                 VectorTile: VectorTile$1,
47688                 VectorTileFeature: VectorTileFeature$1,
47689                 VectorTileLayer: VectorTileLayer$1
47690         };
47691
47692         var tiler$7 = utilTiler().tileSize(512).margin(1);
47693         var dispatch$8 = dispatch('loadedData');
47694         var _vtCache;
47695
47696
47697         function abortRequest$7(controller) {
47698             controller.abort();
47699         }
47700
47701
47702         function vtToGeoJSON(data, tile, mergeCache) {
47703             var vectorTile$1 = new vectorTile.VectorTile(new pbf(data));
47704             var layers = Object.keys(vectorTile$1.layers);
47705             if (!Array.isArray(layers)) { layers = [layers]; }
47706
47707             var features = [];
47708             layers.forEach(function(layerID) {
47709                 var layer = vectorTile$1.layers[layerID];
47710                 if (layer) {
47711                     for (var i = 0; i < layer.length; i++) {
47712                         var feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
47713                         var geometry = feature.geometry;
47714
47715                         // Treat all Polygons as MultiPolygons
47716                         if (geometry.type === 'Polygon') {
47717                             geometry.type = 'MultiPolygon';
47718                             geometry.coordinates = [geometry.coordinates];
47719                         }
47720
47721                         // Clip to tile bounds
47722                         if (geometry.type === 'MultiPolygon') {
47723                             var isClipped = false;
47724                             var featureClip = bboxClip_1(feature, tile.extent.rectangle());
47725                             if (!fastDeepEqual(feature.geometry, featureClip.geometry)) {
47726                                 // feature = featureClip;
47727                                 isClipped = true;
47728                             }
47729                             if (!feature.geometry.coordinates.length) continue;   // not actually on this tile
47730                             if (!feature.geometry.coordinates[0].length) continue;   // not actually on this tile
47731                         }
47732
47733                         // Generate some unique IDs and add some metadata
47734                         var featurehash = utilHashcode(fastJsonStableStringify(feature));
47735                         var propertyhash = utilHashcode(fastJsonStableStringify(feature.properties || {}));
47736                         feature.__layerID__ = layerID.replace(/[^_a-zA-Z0-9\-]/g, '_');
47737                         feature.__featurehash__ = featurehash;
47738                         feature.__propertyhash__ = propertyhash;
47739                         features.push(feature);
47740
47741                         // Clipped Polygons at same zoom with identical properties can get merged
47742                         if (isClipped && geometry.type === 'MultiPolygon') {
47743                             var merged = mergeCache[propertyhash];
47744                             if (merged && merged.length) {
47745                                 var other = merged[0];
47746                                 var coords = union(
47747                                     feature.geometry.coordinates,
47748                                     other.geometry.coordinates
47749                                 );
47750
47751                                 if (!coords || !coords.length) {
47752                                     continue;  // something failed in martinez union
47753                                 }
47754
47755                                 merged.push(feature);
47756                                 for (var j = 0; j < merged.length; j++) {      // all these features get...
47757                                     merged[j].geometry.coordinates = coords;   // same coords
47758                                     merged[j].__featurehash__ = featurehash;   // same hash, so deduplication works
47759                                 }
47760                             } else {
47761                                 mergeCache[propertyhash] = [feature];
47762                             }
47763                         }
47764                     }
47765                 }
47766             });
47767
47768             return features;
47769         }
47770
47771
47772         function loadTile(source, tile) {
47773             if (source.loaded[tile.id] || source.inflight[tile.id]) return;
47774
47775             var url = source.template
47776                 .replace('{x}', tile.xyz[0])
47777                 .replace('{y}', tile.xyz[1])
47778                 // TMS-flipped y coordinate
47779                 .replace(/\{[t-]y\}/, Math.pow(2, tile.xyz[2]) - tile.xyz[1] - 1)
47780                 .replace(/\{z(oom)?\}/, tile.xyz[2])
47781                 .replace(/\{switch:([^}]+)\}/, function(s, r) {
47782                     var subdomains = r.split(',');
47783                     return subdomains[(tile.xyz[0] + tile.xyz[1]) % subdomains.length];
47784                 });
47785
47786
47787             var controller = new AbortController();
47788             source.inflight[tile.id] = controller;
47789
47790             fetch(url, { signal: controller.signal })
47791                 .then(function(response) {
47792                     if (!response.ok) {
47793                         throw new Error(response.status + ' ' + response.statusText);
47794                     }
47795                     source.loaded[tile.id] = [];
47796                     delete source.inflight[tile.id];
47797                     return response.arrayBuffer();
47798                 })
47799                 .then(function(data) {
47800                     if (!data) {
47801                         throw new Error('No Data');
47802                     }
47803
47804                     var z = tile.xyz[2];
47805                     if (!source.canMerge[z]) {
47806                         source.canMerge[z] = {};  // initialize mergeCache
47807                     }
47808
47809                     source.loaded[tile.id] = vtToGeoJSON(data, tile, source.canMerge[z]);
47810                     dispatch$8.call('loadedData');
47811                 })
47812                 .catch(function() {
47813                     source.loaded[tile.id] = [];
47814                     delete source.inflight[tile.id];
47815                 });
47816         }
47817
47818
47819         var serviceVectorTile = {
47820
47821             init: function() {
47822                 if (!_vtCache) {
47823                     this.reset();
47824                 }
47825
47826                 this.event = utilRebind(this, dispatch$8, 'on');
47827             },
47828
47829
47830             reset: function() {
47831                 for (var sourceID in _vtCache) {
47832                     var source = _vtCache[sourceID];
47833                     if (source && source.inflight) {
47834                         Object.values(source.inflight).forEach(abortRequest$7);
47835                     }
47836                 }
47837
47838                 _vtCache = {};
47839             },
47840
47841
47842             addSource: function(sourceID, template) {
47843                 _vtCache[sourceID] = { template: template, inflight: {}, loaded: {}, canMerge: {} };
47844                 return _vtCache[sourceID];
47845             },
47846
47847
47848             data: function(sourceID, projection) {
47849                 var source = _vtCache[sourceID];
47850                 if (!source) return [];
47851
47852                 var tiles = tiler$7.getTiles(projection);
47853                 var seen = {};
47854                 var results = [];
47855
47856                 for (var i = 0; i < tiles.length; i++) {
47857                     var features = source.loaded[tiles[i].id];
47858                     if (!features || !features.length) continue;
47859
47860                     for (var j = 0; j < features.length; j++) {
47861                         var feature = features[j];
47862                         var hash = feature.__featurehash__;
47863                         if (seen[hash]) continue;
47864                         seen[hash] = true;
47865
47866                         // return a shallow copy, because the hash may change
47867                         // later if this feature gets merged with another
47868                         results.push(Object.assign({}, feature));  // shallow copy
47869                     }
47870                 }
47871
47872                 return results;
47873             },
47874
47875
47876             loadTiles: function(sourceID, template, projection) {
47877                 var source = _vtCache[sourceID];
47878                 if (!source) {
47879                     source = this.addSource(sourceID, template);
47880                 }
47881
47882                 var tiles = tiler$7.getTiles(projection);
47883
47884                 // abort inflight requests that are no longer needed
47885                 Object.keys(source.inflight).forEach(function(k) {
47886                     var wanted = tiles.find(function(tile) { return k === tile.id; });
47887                     if (!wanted) {
47888                         abortRequest$7(source.inflight[k]);
47889                         delete source.inflight[k];
47890                     }
47891                 });
47892
47893                 tiles.forEach(function(tile) {
47894                     loadTile(source, tile);
47895                 });
47896             },
47897
47898
47899             cache: function() {
47900                 return _vtCache;
47901             }
47902
47903         };
47904
47905         var apibase$5 = 'https://www.wikidata.org/w/api.php?';
47906         var _wikidataCache = {};
47907
47908
47909         var serviceWikidata = {
47910
47911             init: function() {},
47912
47913             reset: function() {
47914                 _wikidataCache = {};
47915             },
47916
47917
47918             // Search for Wikidata items matching the query
47919             itemsForSearchQuery: function(query, callback) {
47920                 if (!query) {
47921                     if (callback) callback('No query', {});
47922                     return;
47923                 }
47924
47925                 var lang = this.languagesToQuery()[0];
47926
47927                 var url = apibase$5 + utilQsString({
47928                     action: 'wbsearchentities',
47929                     format: 'json',
47930                     formatversion: 2,
47931                     search: query,
47932                     type: 'item',
47933                     // the language to search
47934                     language: lang,
47935                     // the langauge for the label and description in the result
47936                     uselang: lang,
47937                     limit: 10,
47938                     origin: '*'
47939                 });
47940
47941                 d3_json(url)
47942                     .then(function(result) {
47943                         if (result && result.error) {
47944                             throw new Error(result.error);
47945                         }
47946                         if (callback) callback(null, result.search || {});
47947                     })
47948                     .catch(function(err) {
47949                         if (callback) callback(err.message, {});
47950                     });
47951             },
47952
47953
47954             // Given a Wikipedia language and article title,
47955             // return an array of corresponding Wikidata entities.
47956             itemsByTitle: function(lang, title, callback) {
47957                 if (!title) {
47958                     if (callback) callback('No title', {});
47959                     return;
47960                 }
47961
47962                 lang = lang || 'en';
47963                 var url = apibase$5 + utilQsString({
47964                     action: 'wbgetentities',
47965                     format: 'json',
47966                     formatversion: 2,
47967                     sites: lang.replace(/-/g, '_') + 'wiki',
47968                     titles: title,
47969                     languages: 'en', // shrink response by filtering to one language
47970                     origin: '*'
47971                 });
47972
47973                 d3_json(url)
47974                     .then(function(result) {
47975                         if (result && result.error) {
47976                             throw new Error(result.error);
47977                         }
47978                         if (callback) callback(null, result.entities || {});
47979                     })
47980                     .catch(function(err) {
47981                         if (callback) callback(err.message, {});
47982                     });
47983             },
47984
47985
47986             languagesToQuery: function() {
47987                 var localeCode = _mainLocalizer.localeCode().toLowerCase();
47988                 // HACK: en-us isn't a wikidata language. We should really be filtering by
47989                 // the languages known to be supported by wikidata.
47990                 if (localeCode === 'en-us') localeCode = 'en';
47991                 return utilArrayUniq([
47992                     localeCode,
47993                     _mainLocalizer.languageCode().toLowerCase(),
47994                     'en'
47995                 ]);
47996             },
47997
47998
47999             entityByQID: function(qid, callback) {
48000                 if (!qid) {
48001                     callback('No qid', {});
48002                     return;
48003                 }
48004                 if (_wikidataCache[qid]) {
48005                     if (callback) callback(null, _wikidataCache[qid]);
48006                     return;
48007                 }
48008
48009                 var langs = this.languagesToQuery();
48010                 var url = apibase$5 + utilQsString({
48011                     action: 'wbgetentities',
48012                     format: 'json',
48013                     formatversion: 2,
48014                     ids: qid,
48015                     props: 'labels|descriptions|claims|sitelinks',
48016                     sitefilter: langs.map(function(d) { return d + 'wiki'; }).join('|'),
48017                     languages: langs.join('|'),
48018                     languagefallback: 1,
48019                     origin: '*'
48020                 });
48021
48022                 d3_json(url)
48023                     .then(function(result) {
48024                         if (result && result.error) {
48025                             throw new Error(result.error);
48026                         }
48027                         if (callback) callback(null, result.entities[qid] || {});
48028                     })
48029                     .catch(function(err) {
48030                         if (callback) callback(err.message, {});
48031                     });
48032             },
48033
48034
48035             // Pass `params` object of the form:
48036             // {
48037             //   qid: 'string'      // brand wikidata  (e.g. 'Q37158')
48038             // }
48039             //
48040             // Get an result object used to display tag documentation
48041             // {
48042             //   title:        'string',
48043             //   description:  'string',
48044             //   editURL:      'string',
48045             //   imageURL:     'string',
48046             //   wiki:         { title: 'string', text: 'string', url: 'string' }
48047             // }
48048             //
48049             getDocs: function(params, callback) {
48050                 var langs = this.languagesToQuery();
48051                 this.entityByQID(params.qid, function(err, entity) {
48052                     if (err || !entity) {
48053                         callback(err || 'No entity');
48054                         return;
48055                     }
48056
48057                     var i;
48058                     var description;
48059                     if (entity.descriptions && Object.keys(entity.descriptions).length > 0) {
48060                         description = entity.descriptions[Object.keys(entity.descriptions)[0]].value;
48061                     }
48062
48063                     // prepare result
48064                     var result = {
48065                         title: entity.id,
48066                         description: description,
48067                         editURL: 'https://www.wikidata.org/wiki/' + entity.id
48068                     };
48069
48070                     // add image
48071                     if (entity.claims) {
48072                         var imageroot = 'https://commons.wikimedia.org/w/index.php';
48073                         var props = ['P154','P18'];  // logo image, image
48074                         var prop, image;
48075                         for (i = 0; i < props.length; i++) {
48076                             prop = entity.claims[props[i]];
48077                             if (prop && Object.keys(prop).length > 0) {
48078                                 image = prop[Object.keys(prop)[0]].mainsnak.datavalue.value;
48079                                 if (image) {
48080                                     result.imageURL = imageroot + '?' + utilQsString({
48081                                         title: 'Special:Redirect/file/' + image,
48082                                         width: 400
48083                                     });
48084                                     break;
48085                                 }
48086                             }
48087                         }
48088                     }
48089
48090                     if (entity.sitelinks) {
48091                         var englishLocale = _mainLocalizer.languageCode().toLowerCase() === 'en';
48092
48093                         // must be one of these that we requested..
48094                         for (i = 0; i < langs.length; i++) {   // check each, in order of preference
48095                             var w = langs[i] + 'wiki';
48096                             if (entity.sitelinks[w]) {
48097                                 var title = entity.sitelinks[w].title;
48098                                 var tKey = 'inspector.wiki_reference';
48099                                 if (!englishLocale && langs[i] === 'en') {   // user's locale isn't English but
48100                                     tKey = 'inspector.wiki_en_reference';    // we are sending them to enwiki anyway..
48101                                 }
48102
48103                                 result.wiki = {
48104                                     title: title,
48105                                     text: tKey,
48106                                     url: 'https://' + langs[i] + '.wikipedia.org/wiki/' + title.replace(/ /g, '_')
48107                                 };
48108                                 break;
48109                             }
48110                         }
48111                     }
48112
48113                     callback(null, result);
48114                 });
48115             }
48116
48117         };
48118
48119         var endpoint = 'https://en.wikipedia.org/w/api.php?';
48120
48121         var serviceWikipedia = {
48122
48123             init: function() {},
48124             reset: function() {},
48125
48126
48127             search: function(lang, query, callback) {
48128                 if (!query) {
48129                     if (callback) callback('No Query', []);
48130                     return;
48131                 }
48132
48133                 lang = lang || 'en';
48134                 var url = endpoint.replace('en', lang) +
48135                     utilQsString({
48136                         action: 'query',
48137                         list: 'search',
48138                         srlimit: '10',
48139                         srinfo: 'suggestion',
48140                         format: 'json',
48141                         origin: '*',
48142                         srsearch: query
48143                     });
48144
48145                 d3_json(url)
48146                     .then(function(result) {
48147                         if (result && result.error) {
48148                             throw new Error(result.error);
48149                         } else if (!result || !result.query || !result.query.search) {
48150                             throw new Error('No Results');
48151                         }
48152                         if (callback) {
48153                             var titles = result.query.search.map(function(d) { return d.title; });
48154                             callback(null, titles);
48155                         }
48156                     })
48157                     .catch(function(err) {
48158                         if (callback) callback(err, []);
48159                     });
48160             },
48161
48162
48163             suggestions: function(lang, query, callback) {
48164                 if (!query) {
48165                     if (callback) callback('', []);
48166                     return;
48167                 }
48168
48169                 lang = lang || 'en';
48170                 var url = endpoint.replace('en', lang) +
48171                     utilQsString({
48172                         action: 'opensearch',
48173                         namespace: 0,
48174                         suggest: '',
48175                         format: 'json',
48176                         origin: '*',
48177                         search: query
48178                     });
48179
48180                 d3_json(url)
48181                     .then(function(result) {
48182                         if (result && result.error) {
48183                             throw new Error(result.error);
48184                         } else if (!result || result.length < 2) {
48185                             throw new Error('No Results');
48186                         }
48187                         if (callback) callback(null, result[1] || []);
48188                     })
48189                     .catch(function(err) {
48190                         if (callback) callback(err.message, []);
48191                     });
48192             },
48193
48194
48195             translations: function(lang, title, callback) {
48196                 if (!title) {
48197                     if (callback) callback('No Title');
48198                     return;
48199                 }
48200
48201                 var url = endpoint.replace('en', lang) +
48202                     utilQsString({
48203                         action: 'query',
48204                         prop: 'langlinks',
48205                         format: 'json',
48206                         origin: '*',
48207                         lllimit: 500,
48208                         titles: title
48209                     });
48210
48211                 d3_json(url)
48212                     .then(function(result) {
48213                         if (result && result.error) {
48214                             throw new Error(result.error);
48215                         } else if (!result || !result.query || !result.query.pages) {
48216                             throw new Error('No Results');
48217                         }
48218                         if (callback) {
48219                             var list = result.query.pages[Object.keys(result.query.pages)[0]];
48220                             var translations = {};
48221                             if (list && list.langlinks) {
48222                                 list.langlinks.forEach(function(d) { translations[d.lang] = d['*']; });
48223                             }
48224                             callback(null, translations);
48225                         }
48226                     })
48227                     .catch(function(err) {
48228                         if (callback) callback(err.message);
48229                     });
48230             }
48231
48232         };
48233
48234         var services = {
48235             geocoder: serviceNominatim,
48236             keepRight: serviceKeepRight,
48237             improveOSM: serviceImproveOSM,
48238             osmose: serviceOsmose,
48239             mapillary: serviceMapillary,
48240             openstreetcam: serviceOpenstreetcam,
48241             osm: serviceOsm,
48242             osmWikibase: serviceOsmWikibase,
48243             maprules: serviceMapRules,
48244             streetside: serviceStreetside,
48245             taginfo: serviceTaginfo,
48246             vectorTile: serviceVectorTile,
48247             wikidata: serviceWikidata,
48248             wikipedia: serviceWikipedia
48249         };
48250
48251         function svgIcon(name, svgklass, useklass) {
48252             return function drawIcon(selection) {
48253                 selection.selectAll('svg.icon' + (svgklass ? '.' + svgklass.split(' ')[0] : ''))
48254                     .data([0])
48255                     .enter()
48256                     .append('svg')
48257                     .attr('class', 'icon ' + (svgklass || ''))
48258                     .append('use')
48259                     .attr('xlink:href', name)
48260                     .attr('class', useklass);
48261             };
48262         }
48263
48264         function uiNoteComments() {
48265             var _note;
48266
48267
48268             function noteComments(selection) {
48269                 if (_note.isNew()) return; // don't draw .comments-container
48270
48271                 var comments = selection.selectAll('.comments-container')
48272                     .data([0]);
48273
48274                 comments = comments.enter()
48275                     .append('div')
48276                     .attr('class', 'comments-container')
48277                     .merge(comments);
48278
48279                 var commentEnter = comments.selectAll('.comment')
48280                     .data(_note.comments)
48281                     .enter()
48282                     .append('div')
48283                     .attr('class', 'comment');
48284
48285                 commentEnter
48286                     .append('div')
48287                     .attr('class', function(d) { return 'comment-avatar user-' + d.uid; })
48288                     .call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
48289
48290                 var mainEnter = commentEnter
48291                     .append('div')
48292                     .attr('class', 'comment-main');
48293
48294                 var metadataEnter = mainEnter
48295                     .append('div')
48296                     .attr('class', 'comment-metadata');
48297
48298                 metadataEnter
48299                     .append('div')
48300                     .attr('class', 'comment-author')
48301                     .each(function(d) {
48302                         var selection = select(this);
48303                         var osm = services.osm;
48304                         if (osm && d.user) {
48305                             selection = selection
48306                                 .append('a')
48307                                 .attr('class', 'comment-author-link')
48308                                 .attr('href', osm.userURL(d.user))
48309                                 .attr('tabindex', -1)
48310                                 .attr('target', '_blank');
48311                         }
48312                         selection
48313                             .text(function(d) { return d.user || _t('note.anonymous'); });
48314                     });
48315
48316                 metadataEnter
48317                     .append('div')
48318                     .attr('class', 'comment-date')
48319                     .text(function(d) {
48320                         return _t('note.status.' + d.action, { when: localeDateString(d.date) });
48321                     });
48322
48323                 mainEnter
48324                     .append('div')
48325                     .attr('class', 'comment-text')
48326                     .html(function(d) { return d.html; });
48327
48328                 comments
48329                     .call(replaceAvatars);
48330             }
48331
48332
48333             function replaceAvatars(selection) {
48334                 var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
48335                 var osm = services.osm;
48336                 if (showThirdPartyIcons !== 'true' || !osm) return;
48337
48338                 var uids = {};  // gather uids in the comment thread
48339                 _note.comments.forEach(function(d) {
48340                     if (d.uid) uids[d.uid] = true;
48341                 });
48342
48343                 Object.keys(uids).forEach(function(uid) {
48344                     osm.loadUser(uid, function(err, user) {
48345                         if (!user || !user.image_url) return;
48346
48347                         selection.selectAll('.comment-avatar.user-' + uid)
48348                             .html('')
48349                             .append('img')
48350                             .attr('class', 'icon comment-avatar-icon')
48351                             .attr('src', user.image_url)
48352                             .attr('alt', user.display_name);
48353                     });
48354                 });
48355             }
48356
48357
48358             function localeDateString(s) {
48359                 if (!s) return null;
48360                 var options = { day: 'numeric', month: 'short', year: 'numeric' };
48361                 s = s.replace(/-/g, '/'); // fix browser-specific Date() issues
48362                 var d = new Date(s);
48363                 if (isNaN(d.getTime())) return null;
48364                 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
48365             }
48366
48367
48368             noteComments.note = function(val) {
48369                 if (!arguments.length) return _note;
48370                 _note = val;
48371                 return noteComments;
48372             };
48373
48374
48375             return noteComments;
48376         }
48377
48378         function uiNoteHeader() {
48379             var _note;
48380
48381
48382             function noteHeader(selection) {
48383                 var header = selection.selectAll('.note-header')
48384                     .data(
48385                         (_note ? [_note] : []),
48386                         function(d) { return d.status + d.id; }
48387                     );
48388
48389                 header.exit()
48390                     .remove();
48391
48392                 var headerEnter = header.enter()
48393                     .append('div')
48394                     .attr('class', 'note-header');
48395
48396                 var iconEnter = headerEnter
48397                     .append('div')
48398                     .attr('class', function(d) { return 'note-header-icon ' + d.status; })
48399                     .classed('new', function(d) { return d.id < 0; });
48400
48401                 iconEnter
48402                     .append('div')
48403                     .attr('class', 'preset-icon-28')
48404                     .call(svgIcon('#iD-icon-note', 'note-fill'));
48405
48406                 iconEnter.each(function(d) {
48407                     var statusIcon = '#iD-icon-' + (d.id < 0 ? 'plus' : (d.status === 'open' ? 'close' : 'apply'));
48408                     iconEnter
48409                         .append('div')
48410                         .attr('class', 'note-icon-annotation')
48411                         .call(svgIcon(statusIcon, 'icon-annotation'));
48412                 });
48413
48414                 headerEnter
48415                     .append('div')
48416                     .attr('class', 'note-header-label')
48417                     .text(function(d) {
48418                         if (_note.isNew()) { return _t('note.new'); }
48419                         return _t('note.note') + ' ' + d.id + ' ' +
48420                             (d.status === 'closed' ? _t('note.closed') : '');
48421                     });
48422             }
48423
48424
48425             noteHeader.note = function(val) {
48426                 if (!arguments.length) return _note;
48427                 _note = val;
48428                 return noteHeader;
48429             };
48430
48431
48432             return noteHeader;
48433         }
48434
48435         function uiNoteReport() {
48436             var _note;
48437
48438             function noteReport(selection) {
48439                 var url;
48440                 if (services.osm && (_note instanceof osmNote) && (!_note.isNew())) {
48441                     url = services.osm.noteReportURL(_note);
48442                 }
48443
48444                 var link = selection.selectAll('.note-report')
48445                     .data(url ? [url] : []);
48446
48447                 // exit
48448                 link.exit()
48449                     .remove();
48450
48451                 // enter
48452                 var linkEnter = link.enter()
48453                     .append('a')
48454                     .attr('class', 'note-report')
48455                     .attr('target', '_blank')
48456                     .attr('href', function(d) { return d; })
48457                     .call(svgIcon('#iD-icon-out-link', 'inline'));
48458
48459                 linkEnter
48460                     .append('span')
48461                     .text(_t('note.report'));
48462             }
48463
48464
48465             noteReport.note = function(val) {
48466                 if (!arguments.length) return _note;
48467                 _note = val;
48468                 return noteReport;
48469             };
48470
48471             return noteReport;
48472         }
48473
48474         function uiViewOnOSM(context) {
48475             var _what;   // an osmEntity or osmNote
48476
48477
48478             function viewOnOSM(selection) {
48479                 var url;
48480                 if (_what instanceof osmEntity) {
48481                     url = context.connection().entityURL(_what);
48482                 } else if (_what instanceof osmNote) {
48483                     url = context.connection().noteURL(_what);
48484                 }
48485
48486                 var data = ((!_what || _what.isNew()) ? [] : [_what]);
48487                 var link = selection.selectAll('.view-on-osm')
48488                     .data(data, function(d) { return d.id; });
48489
48490                 // exit
48491                 link.exit()
48492                     .remove();
48493
48494                 // enter
48495                 var linkEnter = link.enter()
48496                     .append('a')
48497                     .attr('class', 'view-on-osm')
48498                     .attr('target', '_blank')
48499                     .attr('href', url)
48500                     .call(svgIcon('#iD-icon-out-link', 'inline'));
48501
48502                 linkEnter
48503                     .append('span')
48504                     .text(_t('inspector.view_on_osm'));
48505             }
48506
48507
48508             viewOnOSM.what = function(_) {
48509                 if (!arguments.length) return _what;
48510                 _what = _;
48511                 return viewOnOSM;
48512             };
48513
48514             return viewOnOSM;
48515         }
48516
48517         function uiNoteEditor(context) {
48518             var dispatch$1 = dispatch('change');
48519             var noteComments = uiNoteComments();
48520             var noteHeader = uiNoteHeader();
48521
48522             // var formFields = uiFormFields(context);
48523
48524             var _note;
48525             var _newNote;
48526             // var _fieldsArr;
48527
48528
48529             function noteEditor(selection) {
48530
48531                 var header = selection.selectAll('.header')
48532                     .data([0]);
48533
48534                 var headerEnter = header.enter()
48535                     .append('div')
48536                     .attr('class', 'header fillL');
48537
48538                 headerEnter
48539                     .append('button')
48540                     .attr('class', 'close')
48541                     .on('click', function() {
48542                         context.enter(modeBrowse(context));
48543                     })
48544                     .call(svgIcon('#iD-icon-close'));
48545
48546                 headerEnter
48547                     .append('h3')
48548                     .text(_t('note.title'));
48549
48550
48551                 var body = selection.selectAll('.body')
48552                     .data([0]);
48553
48554                 body = body.enter()
48555                     .append('div')
48556                     .attr('class', 'body')
48557                     .merge(body);
48558
48559                 var editor = body.selectAll('.note-editor')
48560                     .data([0]);
48561
48562                 editor.enter()
48563                     .append('div')
48564                     .attr('class', 'modal-section note-editor')
48565                     .merge(editor)
48566                     .call(noteHeader.note(_note))
48567                     .call(noteComments.note(_note))
48568                     .call(noteSaveSection);
48569
48570                 var footer = selection.selectAll('.footer')
48571                     .data([0]);
48572
48573                 footer.enter()
48574                     .append('div')
48575                     .attr('class', 'footer')
48576                     .merge(footer)
48577                     .call(uiViewOnOSM(context).what(_note))
48578                     .call(uiNoteReport().note(_note));
48579
48580
48581                 // rerender the note editor on any auth change
48582                 var osm = services.osm;
48583                 if (osm) {
48584                     osm.on('change.note-save', function() {
48585                         selection.call(noteEditor);
48586                     });
48587                 }
48588             }
48589
48590
48591             function noteSaveSection(selection) {
48592                 var isSelected = (_note && _note.id === context.selectedNoteID());
48593                 var noteSave = selection.selectAll('.note-save')
48594                     .data((isSelected ? [_note] : []), function(d) { return d.status + d.id; });
48595
48596                 // exit
48597                 noteSave.exit()
48598                     .remove();
48599
48600                 // enter
48601                 var noteSaveEnter = noteSave.enter()
48602                     .append('div')
48603                     .attr('class', 'note-save save-section cf');
48604
48605                 // // if new note, show categories to pick from
48606                 // if (_note.isNew()) {
48607                 //     var presets = presetManager;
48608
48609                 //     // NOTE: this key isn't a age and therefore there is no documentation (yet)
48610                 //     _fieldsArr = [
48611                 //         uiField(context, presets.field('category'), null, { show: true, revert: false }),
48612                 //     ];
48613
48614                 //     _fieldsArr.forEach(function(field) {
48615                 //         field
48616                 //             .on('change', changeCategory);
48617                 //     });
48618
48619                 //     noteSaveEnter
48620                 //         .append('div')
48621                 //         .attr('class', 'note-category')
48622                 //         .call(formFields.fieldsArr(_fieldsArr));
48623                 // }
48624
48625                 // function changeCategory() {
48626                 //     // NOTE: perhaps there is a better way to get value
48627                 //     var val = context.container().select('input[name=\'category\']:checked').property('__data__') || undefined;
48628
48629                 //     // store the unsaved category with the note itself
48630                 //     _note = _note.update({ newCategory: val });
48631                 //     var osm = services.osm;
48632                 //     if (osm) {
48633                 //         osm.replaceNote(_note);  // update note cache
48634                 //     }
48635                 //     noteSave
48636                 //         .call(noteSaveButtons);
48637                 // }
48638
48639                 noteSaveEnter
48640                     .append('h4')
48641                     .attr('class', '.note-save-header')
48642                     .text(function() {
48643                         return _note.isNew() ? _t('note.newDescription') : _t('note.newComment');
48644                     });
48645
48646                 var commentTextarea = noteSaveEnter
48647                     .append('textarea')
48648                     .attr('class', 'new-comment-input')
48649                     .attr('placeholder', _t('note.inputPlaceholder'))
48650                     .attr('maxlength', 1000)
48651                     .property('value', function(d) { return d.newComment; })
48652                     .call(utilNoAuto)
48653                     .on('keydown.note-input', keydown)
48654                     .on('input.note-input', changeInput)
48655                     .on('blur.note-input', changeInput);
48656
48657                 if (_newNote) {
48658                     // autofocus the comment field for new notes
48659                     commentTextarea.node().focus();
48660                 }
48661
48662                 // update
48663                 noteSave = noteSaveEnter
48664                     .merge(noteSave)
48665                     .call(userDetails)
48666                     .call(noteSaveButtons);
48667
48668
48669                 // fast submit if user presses cmd+enter
48670                 function keydown() {
48671                     if (!(event.keyCode === 13 && event.metaKey)) return;
48672
48673                     var osm = services.osm;
48674                     if (!osm) return;
48675
48676                     var hasAuth = osm.authenticated();
48677                     if (!hasAuth) return;
48678
48679                     if (!_note.newComment) return;
48680
48681                     event.preventDefault();
48682
48683                     select(this)
48684                         .on('keydown.note-input', null);
48685
48686                     // focus on button and submit
48687                     window.setTimeout(function() {
48688                         if (_note.isNew()) {
48689                             noteSave.selectAll('.save-button').node().focus();
48690                             clickSave(_note);
48691                         } else  {
48692                             noteSave.selectAll('.comment-button').node().focus();
48693                             clickComment(_note);
48694                         }
48695                     }, 10);
48696                 }
48697
48698
48699                 function changeInput() {
48700                     var input = select(this);
48701                     var val = input.property('value').trim() || undefined;
48702
48703                     // store the unsaved comment with the note itself
48704                     _note = _note.update({ newComment: val });
48705
48706                     var osm = services.osm;
48707                     if (osm) {
48708                         osm.replaceNote(_note);  // update note cache
48709                     }
48710
48711                     noteSave
48712                         .call(noteSaveButtons);
48713                 }
48714             }
48715
48716
48717             function userDetails(selection) {
48718                 var detailSection = selection.selectAll('.detail-section')
48719                     .data([0]);
48720
48721                 detailSection = detailSection.enter()
48722                     .append('div')
48723                     .attr('class', 'detail-section')
48724                     .merge(detailSection);
48725
48726                 var osm = services.osm;
48727                 if (!osm) return;
48728
48729                 // Add warning if user is not logged in
48730                 var hasAuth = osm.authenticated();
48731                 var authWarning = detailSection.selectAll('.auth-warning')
48732                     .data(hasAuth ? [] : [0]);
48733
48734                 authWarning.exit()
48735                     .transition()
48736                     .duration(200)
48737                     .style('opacity', 0)
48738                     .remove();
48739
48740                 var authEnter = authWarning.enter()
48741                     .insert('div', '.tag-reference-body')
48742                     .attr('class', 'field-warning auth-warning')
48743                     .style('opacity', 0);
48744
48745                 authEnter
48746                     .call(svgIcon('#iD-icon-alert', 'inline'));
48747
48748                 authEnter
48749                     .append('span')
48750                     .text(_t('note.login'));
48751
48752                 authEnter
48753                     .append('a')
48754                     .attr('target', '_blank')
48755                     .call(svgIcon('#iD-icon-out-link', 'inline'))
48756                     .append('span')
48757                     .text(_t('login'))
48758                     .on('click.note-login', function() {
48759                         event.preventDefault();
48760                         osm.authenticate();
48761                     });
48762
48763                 authEnter
48764                     .transition()
48765                     .duration(200)
48766                     .style('opacity', 1);
48767
48768
48769                 var prose = detailSection.selectAll('.note-save-prose')
48770                     .data(hasAuth ? [0] : []);
48771
48772                 prose.exit()
48773                     .remove();
48774
48775                 prose = prose.enter()
48776                     .append('p')
48777                     .attr('class', 'note-save-prose')
48778                     .text(_t('note.upload_explanation'))
48779                     .merge(prose);
48780
48781                 osm.userDetails(function(err, user) {
48782                     if (err) return;
48783
48784                     var userLink = select(document.createElement('div'));
48785
48786                     if (user.image_url) {
48787                         userLink
48788                             .append('img')
48789                             .attr('src', user.image_url)
48790                             .attr('class', 'icon pre-text user-icon');
48791                     }
48792
48793                     userLink
48794                         .append('a')
48795                         .attr('class', 'user-info')
48796                         .text(user.display_name)
48797                         .attr('href', osm.userURL(user.display_name))
48798                         .attr('tabindex', -1)
48799                         .attr('target', '_blank');
48800
48801                     prose
48802                         .html(_t('note.upload_explanation_with_user', { user: userLink.html() }));
48803                 });
48804             }
48805
48806
48807             function noteSaveButtons(selection) {
48808                 var osm = services.osm;
48809                 var hasAuth = osm && osm.authenticated();
48810
48811                 var isSelected = (_note && _note.id === context.selectedNoteID());
48812                 var buttonSection = selection.selectAll('.buttons')
48813                     .data((isSelected ? [_note] : []), function(d) { return d.status + d.id; });
48814
48815                 // exit
48816                 buttonSection.exit()
48817                     .remove();
48818
48819                 // enter
48820                 var buttonEnter = buttonSection.enter()
48821                     .append('div')
48822                     .attr('class', 'buttons');
48823
48824                 if (_note.isNew()) {
48825                     buttonEnter
48826                         .append('button')
48827                         .attr('class', 'button cancel-button secondary-action')
48828                         .text(_t('confirm.cancel'));
48829
48830                     buttonEnter
48831                         .append('button')
48832                         .attr('class', 'button save-button action')
48833                         .text(_t('note.save'));
48834
48835                 } else {
48836                     buttonEnter
48837                         .append('button')
48838                         .attr('class', 'button status-button action');
48839
48840                     buttonEnter
48841                         .append('button')
48842                         .attr('class', 'button comment-button action')
48843                         .text(_t('note.comment'));
48844                 }
48845
48846
48847                 // update
48848                 buttonSection = buttonSection
48849                     .merge(buttonEnter);
48850
48851                 buttonSection.select('.cancel-button')   // select and propagate data
48852                     .on('click.cancel', clickCancel);
48853
48854                 buttonSection.select('.save-button')     // select and propagate data
48855                     .attr('disabled', isSaveDisabled)
48856                     .on('click.save', clickSave);
48857
48858                 buttonSection.select('.status-button')   // select and propagate data
48859                     .attr('disabled', (hasAuth ? null : true))
48860                     .text(function(d) {
48861                         var action = (d.status === 'open' ? 'close' : 'open');
48862                         var andComment = (d.newComment ? '_comment' : '');
48863                         return _t('note.' + action + andComment);
48864                     })
48865                     .on('click.status', clickStatus);
48866
48867                 buttonSection.select('.comment-button')   // select and propagate data
48868                     .attr('disabled', isSaveDisabled)
48869                     .on('click.comment', clickComment);
48870
48871
48872                 function isSaveDisabled(d) {
48873                     return (hasAuth && d.status === 'open' && d.newComment) ? null : true;
48874                 }
48875             }
48876
48877
48878
48879             function clickCancel(d) {
48880                 this.blur();    // avoid keeping focus on the button - #4641
48881                 var osm = services.osm;
48882                 if (osm) {
48883                     osm.removeNote(d);
48884                 }
48885                 context.enter(modeBrowse(context));
48886                 dispatch$1.call('change');
48887             }
48888
48889
48890             function clickSave(d) {
48891                 this.blur();    // avoid keeping focus on the button - #4641
48892                 var osm = services.osm;
48893                 if (osm) {
48894                     osm.postNoteCreate(d, function(err, note) {
48895                         dispatch$1.call('change', note);
48896                     });
48897                 }
48898             }
48899
48900
48901             function clickStatus(d) {
48902                 this.blur();    // avoid keeping focus on the button - #4641
48903                 var osm = services.osm;
48904                 if (osm) {
48905                     var setStatus = (d.status === 'open' ? 'closed' : 'open');
48906                     osm.postNoteUpdate(d, setStatus, function(err, note) {
48907                         dispatch$1.call('change', note);
48908                     });
48909                 }
48910             }
48911
48912             function clickComment(d) {
48913                 this.blur();    // avoid keeping focus on the button - #4641
48914                 var osm = services.osm;
48915                 if (osm) {
48916                     osm.postNoteUpdate(d, d.status, function(err, note) {
48917                         dispatch$1.call('change', note);
48918                     });
48919                 }
48920             }
48921
48922
48923             noteEditor.note = function(val) {
48924                 if (!arguments.length) return _note;
48925                 _note = val;
48926                 return noteEditor;
48927             };
48928
48929             noteEditor.newNote = function(val) {
48930                 if (!arguments.length) return _newNote;
48931                 _newNote = val;
48932                 return noteEditor;
48933             };
48934
48935
48936             return utilRebind(noteEditor, dispatch$1, 'on');
48937         }
48938
48939         function modeSelectNote(context, selectedNoteID) {
48940             var mode = {
48941                 id: 'select-note',
48942                 button: 'browse'
48943             };
48944
48945             var _keybinding = utilKeybinding('select-note');
48946             var _noteEditor = uiNoteEditor(context)
48947                 .on('change', function() {
48948                     context.map().pan([0,0]);  // trigger a redraw
48949                     var note = checkSelectedID();
48950                     if (!note) return;
48951                     context.ui().sidebar
48952                         .show(_noteEditor.note(note));
48953                 });
48954
48955             var _behaviors = [
48956                 behaviorBreathe(),
48957                 behaviorHover(context),
48958                 behaviorSelect(context),
48959                 behaviorLasso(context),
48960                 modeDragNode(context).behavior,
48961                 modeDragNote(context).behavior
48962             ];
48963
48964             var _newFeature = false;
48965
48966
48967             function checkSelectedID() {
48968                 if (!services.osm) return;
48969                 var note = services.osm.getNote(selectedNoteID);
48970                 if (!note) {
48971                     context.enter(modeBrowse(context));
48972                 }
48973                 return note;
48974             }
48975
48976
48977             // class the note as selected, or return to browse mode if the note is gone
48978             function selectNote(drawn) {
48979                 if (!checkSelectedID()) return;
48980
48981                 var selection = context.surface().selectAll('.layer-notes .note-' + selectedNoteID);
48982
48983                 if (selection.empty()) {
48984                     // Return to browse mode if selected DOM elements have
48985                     // disappeared because the user moved them out of view..
48986                     var source = event && event.type === 'zoom' && event.sourceEvent;
48987                     if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
48988                         context.enter(modeBrowse(context));
48989                     }
48990
48991                 } else {
48992                     selection
48993                         .classed('selected', true);
48994
48995                     context.selectedNoteID(selectedNoteID);
48996                 }
48997             }
48998
48999
49000             function esc() {
49001                 if (context.container().select('.combobox').size()) return;
49002                 context.enter(modeBrowse(context));
49003             }
49004
49005
49006             mode.zoomToSelected = function() {
49007                 if (!services.osm) return;
49008                 var note = services.osm.getNote(selectedNoteID);
49009                 if (note) {
49010                     context.map().centerZoomEase(note.loc, 20);
49011                 }
49012             };
49013
49014
49015             mode.newFeature = function(val) {
49016                 if (!arguments.length) return _newFeature;
49017                 _newFeature = val;
49018                 return mode;
49019             };
49020
49021
49022             mode.enter = function() {
49023                 var note = checkSelectedID();
49024                 if (!note) return;
49025
49026                 _behaviors.forEach(context.install);
49027
49028                 _keybinding
49029                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
49030                     .on('⎋', esc, true);
49031
49032                 select(document)
49033                     .call(_keybinding);
49034
49035                 selectNote();
49036
49037                 var sidebar = context.ui().sidebar;
49038                 sidebar.show(_noteEditor.note(note).newNote(_newFeature));
49039
49040                 // expand the sidebar, avoid obscuring the note if needed
49041                 sidebar.expand(sidebar.intersects(note.extent()));
49042
49043                 context.map()
49044                     .on('drawn.select', selectNote);
49045             };
49046
49047
49048             mode.exit = function() {
49049                 _behaviors.forEach(context.uninstall);
49050
49051                 select(document)
49052                     .call(_keybinding.unbind);
49053
49054                 context.surface()
49055                     .selectAll('.layer-notes .selected')
49056                     .classed('selected hover', false);
49057
49058                 context.map()
49059                     .on('drawn.select', null);
49060
49061                 context.ui().sidebar
49062                     .hide();
49063
49064                 context.selectedNoteID(null);
49065             };
49066
49067
49068             return mode;
49069         }
49070
49071         function modeDragNote(context) {
49072             var mode = {
49073                 id: 'drag-note',
49074                 button: 'browse'
49075             };
49076
49077             var edit = behaviorEdit(context);
49078
49079             var _nudgeInterval;
49080             var _lastLoc;
49081             var _note;    // most current note.. dragged note may have stale datum.
49082
49083
49084             function startNudge(nudge) {
49085                 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
49086                 _nudgeInterval = window.setInterval(function() {
49087                     context.map().pan(nudge);
49088                     doMove(nudge);
49089                 }, 50);
49090             }
49091
49092
49093             function stopNudge() {
49094                 if (_nudgeInterval) {
49095                     window.clearInterval(_nudgeInterval);
49096                     _nudgeInterval = null;
49097                 }
49098             }
49099
49100
49101             function origin(note) {
49102                 return context.projection(note.loc);
49103             }
49104
49105
49106             function start(note) {
49107                 _note = note;
49108                 var osm = services.osm;
49109                 if (osm) {
49110                     // Get latest note from cache.. The marker may have a stale datum bound to it
49111                     // and dragging it around can sometimes delete the users note comment.
49112                     _note = osm.getNote(_note.id);
49113                 }
49114
49115                 context.surface().selectAll('.note-' + _note.id)
49116                     .classed('active', true);
49117
49118                 context.perform(actionNoop());
49119                 context.enter(mode);
49120                 context.selectedNoteID(_note.id);
49121             }
49122
49123
49124             function move() {
49125                 event.sourceEvent.stopPropagation();
49126                 _lastLoc = context.projection.invert(event.point);
49127
49128                 doMove();
49129                 var nudge = geoViewportEdge(event.point, context.map().dimensions());
49130                 if (nudge) {
49131                     startNudge(nudge);
49132                 } else {
49133                     stopNudge();
49134                 }
49135             }
49136
49137
49138             function doMove(nudge) {
49139                 nudge = nudge || [0, 0];
49140
49141                 var currPoint = (event && event.point) || context.projection(_lastLoc);
49142                 var currMouse = geoVecSubtract(currPoint, nudge);
49143                 var loc = context.projection.invert(currMouse);
49144
49145                 _note = _note.move(loc);
49146
49147                 var osm = services.osm;
49148                 if (osm) {
49149                     osm.replaceNote(_note);  // update note cache
49150                 }
49151
49152                 context.replace(actionNoop());   // trigger redraw
49153             }
49154
49155
49156             function end() {
49157                 context.replace(actionNoop());   // trigger redraw
49158
49159                 context
49160                     .selectedNoteID(_note.id)
49161                     .enter(modeSelectNote(context, _note.id));
49162             }
49163
49164
49165             var drag = behaviorDrag()
49166                 .selector('.layer-touch.markers .target.note.new')
49167                 .surface(context.container().select('.main-map').node())
49168                 .origin(origin)
49169                 .on('start', start)
49170                 .on('move', move)
49171                 .on('end', end);
49172
49173
49174             mode.enter = function() {
49175                 context.install(edit);
49176             };
49177
49178
49179             mode.exit = function() {
49180                 context.ui().sidebar.hover.cancel();
49181                 context.uninstall(edit);
49182
49183                 context.surface()
49184                     .selectAll('.active')
49185                     .classed('active', false);
49186
49187                 stopNudge();
49188             };
49189
49190             mode.behavior = drag;
49191
49192             return mode;
49193         }
49194
49195         function uiDataHeader() {
49196             var _datum;
49197
49198
49199             function dataHeader(selection) {
49200                 var header = selection.selectAll('.data-header')
49201                     .data(
49202                         (_datum ? [_datum] : []),
49203                         function(d) { return d.__featurehash__; }
49204                     );
49205
49206                 header.exit()
49207                     .remove();
49208
49209                 var headerEnter = header.enter()
49210                     .append('div')
49211                     .attr('class', 'data-header');
49212
49213                 var iconEnter = headerEnter
49214                     .append('div')
49215                     .attr('class', 'data-header-icon');
49216
49217                 iconEnter
49218                     .append('div')
49219                     .attr('class', 'preset-icon-28')
49220                     .call(svgIcon('#iD-icon-data', 'note-fill'));
49221
49222                 headerEnter
49223                     .append('div')
49224                     .attr('class', 'data-header-label')
49225                     .text(_t('map_data.layers.custom.title'));
49226             }
49227
49228
49229             dataHeader.datum = function(val) {
49230                 if (!arguments.length) return _datum;
49231                 _datum = val;
49232                 return this;
49233             };
49234
49235
49236             return dataHeader;
49237         }
49238
49239         // This code assumes that the combobox values will not have duplicate entries.
49240         // It is keyed on the `value` of the entry. Data should be an array of objects like:
49241         //   [{
49242         //       value:  'display text',  // required
49243         //       title:  'hover text'     // optional
49244         //   }, ...]
49245
49246         var _comboHideTimerID;
49247
49248         function uiCombobox(context, klass) {
49249             var dispatch$1 = dispatch('accept', 'cancel');
49250             var container = context.container();
49251
49252             var _suggestions = [];
49253             var _data = [];
49254             var _fetched = {};
49255             var _selected = null;
49256             var _canAutocomplete = true;
49257             var _caseSensitive = false;
49258             var _cancelFetch = false;
49259             var _minItems = 2;
49260             var _tDown = 0;
49261             var _mouseEnterHandler, _mouseLeaveHandler;
49262
49263             var _fetcher = function(val, cb) {
49264                 cb(_data.filter(function(d) {
49265                     var terms = d.terms || [];
49266                     terms.push(d.value);
49267                     return terms.some(function(term) {
49268                         return term
49269                             .toString()
49270                             .toLowerCase()
49271                             .indexOf(val.toLowerCase()) !== -1;
49272                     });
49273                 }));
49274             };
49275
49276             var combobox = function(input, attachTo) {
49277                 if (!input || input.empty()) return;
49278
49279                 input
49280                     .classed('combobox-input', true)
49281                     .on('focus.combo-input', focus)
49282                     .on('blur.combo-input', blur)
49283                     .on('keydown.combo-input', keydown)
49284                     .on('keyup.combo-input', keyup)
49285                     .on('input.combo-input', change)
49286                     .on('mousedown.combo-input', mousedown)
49287                     .each(function() {
49288                         var parent = this.parentNode;
49289                         var sibling = this.nextSibling;
49290
49291                         select(parent).selectAll('.combobox-caret')
49292                             .filter(function(d) { return d === input.node(); })
49293                             .data([input.node()])
49294                             .enter()
49295                             .insert('div', function() { return sibling; })
49296                             .attr('class', 'combobox-caret')
49297                             .on('mousedown.combo-caret', function() {
49298                                 event.preventDefault(); // don't steal focus from input
49299                                 input.node().focus(); // focus the input as if it was clicked
49300                                 mousedown();
49301                             })
49302                             .on('mouseup.combo-caret', function() {
49303                                 event.preventDefault(); // don't steal focus from input
49304                                 mouseup();
49305                             });
49306                     });
49307
49308
49309                 function mousedown() {
49310                     if (event.button !== 0) return;    // left click only
49311                     _tDown = +new Date();
49312
49313                     // clear selection
49314                     var start = input.property('selectionStart');
49315                     var end = input.property('selectionEnd');
49316                     if (start !== end) {
49317                         var val = utilGetSetValue(input);
49318                         input.node().setSelectionRange(val.length, val.length);
49319                         return;
49320                     }
49321
49322                     input.on('mouseup.combo-input', mouseup);
49323                 }
49324
49325
49326                 function mouseup() {
49327                     input.on('mouseup.combo-input', null);
49328                     if (event.button !== 0) return;    // left click only
49329                     if (input.node() !== document.activeElement) return;   // exit if this input is not focused
49330
49331                     var start = input.property('selectionStart');
49332                     var end = input.property('selectionEnd');
49333                     if (start !== end) return;  // exit if user is selecting
49334
49335                     // not showing or showing for a different field - try to show it.
49336                     var combo = container.selectAll('.combobox');
49337                     if (combo.empty() || combo.datum() !== input.node()) {
49338                         var tOrig = _tDown;
49339                         window.setTimeout(function() {
49340                             if (tOrig !== _tDown) return;   // exit if user double clicked
49341                             fetchComboData('', function() {
49342                                 show();
49343                                 render();
49344                             });
49345                         }, 250);
49346
49347                     } else {
49348                         hide();
49349                     }
49350                 }
49351
49352
49353                 function focus() {
49354                     fetchComboData('');   // prefetch values (may warm taginfo cache)
49355                 }
49356
49357
49358                 function blur() {
49359                     _comboHideTimerID = window.setTimeout(hide, 75);
49360                 }
49361
49362
49363                 function show() {
49364                     hide();   // remove any existing
49365
49366                     container
49367                         .insert('div', ':first-child')
49368                         .datum(input.node())
49369                         .attr('class', 'combobox' + (klass ? ' combobox-' + klass : ''))
49370                         .style('position', 'absolute')
49371                         .style('display', 'block')
49372                         .style('left', '0px')
49373                         .on('mousedown.combo-container', function () {
49374                             // prevent moving focus out of the input field
49375                             event.preventDefault();
49376                         });
49377
49378                     container
49379                         .on('scroll.combo-scroll', render, true);
49380                 }
49381
49382
49383                 function hide() {
49384                     if (_comboHideTimerID) {
49385                         window.clearTimeout(_comboHideTimerID);
49386                         _comboHideTimerID = undefined;
49387                     }
49388
49389                     container.selectAll('.combobox')
49390                         .remove();
49391
49392                     container
49393                         .on('scroll.combo-scroll', null);
49394                 }
49395
49396
49397                 function keydown() {
49398                     var shown = !container.selectAll('.combobox').empty();
49399                     var tagName = input.node() ? input.node().tagName.toLowerCase() : '';
49400
49401                     switch (event.keyCode) {
49402                         case 8:   // ⌫ Backspace
49403                         case 46:  // ⌦ Delete
49404                             event.stopPropagation();
49405                             _selected = null;
49406                             render();
49407                             input.on('input.combo-input', function() {
49408                                 var start = input.property('selectionStart');
49409                                 input.node().setSelectionRange(start, start);
49410                                 input.on('input.combo-input', change);
49411                             });
49412                             break;
49413
49414                         case 9:   // ⇥ Tab
49415                             accept();
49416                             break;
49417
49418                         case 13:  // ↩ Return
49419                             event.preventDefault();
49420                             event.stopPropagation();
49421                             break;
49422
49423                         case 38:  // ↑ Up arrow
49424                             if (tagName === 'textarea' && !shown) return;
49425                             event.preventDefault();
49426                             if (tagName === 'input' && !shown) {
49427                                 show();
49428                             }
49429                             nav(-1);
49430                             break;
49431
49432                         case 40:  // ↓ Down arrow
49433                             if (tagName === 'textarea' && !shown) return;
49434                             event.preventDefault();
49435                             if (tagName === 'input' && !shown) {
49436                                 show();
49437                             }
49438                             nav(+1);
49439                             break;
49440                     }
49441                 }
49442
49443
49444                 function keyup() {
49445                     switch (event.keyCode) {
49446                         case 27:  // ⎋ Escape
49447                             cancel();
49448                             break;
49449
49450                         case 13:  // ↩ Return
49451                             accept();
49452                             break;
49453                     }
49454                 }
49455
49456
49457                 // Called whenever the input value is changed (e.g. on typing)
49458                 function change() {
49459                     fetchComboData(value(), function() {
49460                         _selected = null;
49461                         var val = input.property('value');
49462
49463                         if (_suggestions.length) {
49464                             if (input.property('selectionEnd') === val.length) {
49465                                 _selected = tryAutocomplete();
49466                             }
49467
49468                             if (!_selected) {
49469                                 _selected = val;
49470                             }
49471                         }
49472
49473                         if (val.length) {
49474                             var combo = container.selectAll('.combobox');
49475                             if (combo.empty()) {
49476                                 show();
49477                             }
49478                         } else {
49479                             hide();
49480                         }
49481
49482                         render();
49483                     });
49484                 }
49485
49486
49487                 // Called when the user presses up/down arrows to navigate the list
49488                 function nav(dir) {
49489                     if (_suggestions.length) {
49490                         // try to determine previously selected index..
49491                         var index = -1;
49492                         for (var i = 0; i < _suggestions.length; i++) {
49493                             if (_selected && _suggestions[i].value === _selected) {
49494                                 index = i;
49495                                 break;
49496                             }
49497                         }
49498
49499                         // pick new _selected
49500                         index = Math.max(Math.min(index + dir, _suggestions.length - 1), 0);
49501                         _selected = _suggestions[index].value;
49502                         input.property('value', _selected);
49503                     }
49504
49505                     render();
49506                     ensureVisible();
49507                 }
49508
49509
49510                 function ensureVisible() {
49511                     var combo = container.selectAll('.combobox');
49512                     if (combo.empty()) return;
49513
49514                     var containerRect = container.node().getBoundingClientRect();
49515                     var comboRect = combo.node().getBoundingClientRect();
49516
49517                     if (comboRect.bottom > containerRect.bottom) {
49518                         var node = attachTo ? attachTo.node() : input.node();
49519                         node.scrollIntoView({ behavior: 'instant', block: 'center' });
49520                         render();
49521                     }
49522
49523                     // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move
49524                     var selected = combo.selectAll('.combobox-option.selected').node();
49525                     if (selected) {
49526                         selected.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
49527                     }
49528                 }
49529
49530
49531                 function value() {
49532                     var value = input.property('value');
49533                     var start = input.property('selectionStart');
49534                     var end = input.property('selectionEnd');
49535
49536                     if (start && end) {
49537                         value = value.substring(0, start);
49538                     }
49539
49540                     return value;
49541                 }
49542
49543
49544                 function fetchComboData(v, cb) {
49545                     _cancelFetch = false;
49546
49547                     _fetcher.call(input, v, function(results) {
49548                         // already chose a value, don't overwrite or autocomplete it
49549                         if (_cancelFetch) return;
49550
49551                         _suggestions = results;
49552                         results.forEach(function(d) { _fetched[d.value] = d; });
49553
49554                         if (cb) {
49555                             cb();
49556                         }
49557                     });
49558                 }
49559
49560
49561                 function tryAutocomplete() {
49562                     if (!_canAutocomplete) return;
49563
49564                     var val = _caseSensitive ? value() : value().toLowerCase();
49565                     if (!val) return;
49566
49567                     // Don't autocomplete if user is typing a number - #4935
49568                     if (!isNaN(parseFloat(val)) && isFinite(val)) return;
49569
49570                     var bestIndex = -1;
49571                     for (var i = 0; i < _suggestions.length; i++) {
49572                         var suggestion = _suggestions[i].value;
49573                         var compare = _caseSensitive ? suggestion : suggestion.toLowerCase();
49574
49575                         // if search string matches suggestion exactly, pick it..
49576                         if (compare === val) {
49577                             bestIndex = i;
49578                             break;
49579
49580                         // otherwise lock in the first result that starts with the search string..
49581                         } else if (bestIndex === -1 && compare.indexOf(val) === 0) {
49582                             bestIndex = i;
49583                         }
49584                     }
49585
49586                     if (bestIndex !== -1) {
49587                         var bestVal = _suggestions[bestIndex].value;
49588                         input.property('value', bestVal);
49589                         input.node().setSelectionRange(val.length, bestVal.length);
49590                         return bestVal;
49591                     }
49592                 }
49593
49594
49595                 function render() {
49596                     if (_suggestions.length < _minItems || document.activeElement !== input.node()) {
49597                         hide();
49598                         return;
49599                     }
49600
49601                     var shown = !container.selectAll('.combobox').empty();
49602                     if (!shown) return;
49603
49604                     var combo = container.selectAll('.combobox');
49605                     var options = combo.selectAll('.combobox-option')
49606                         .data(_suggestions, function(d) { return d.value; });
49607
49608                     options.exit()
49609                         .remove();
49610
49611                     // enter/update
49612                     options.enter()
49613                         .append('a')
49614                         .attr('class', 'combobox-option')
49615                         .attr('title', function(d) { return d.title; })
49616                         .text(function(d) { return d.display || d.value; })
49617                         .on('mouseenter', _mouseEnterHandler)
49618                         .on('mouseleave', _mouseLeaveHandler)
49619                         .merge(options)
49620                         .classed('selected', function(d) { return d.value === _selected; })
49621                         .on('click.combo-option', accept)
49622                         .order();
49623
49624                     var node = attachTo ? attachTo.node() : input.node();
49625                     var containerRect = container.node().getBoundingClientRect();
49626                     var rect = node.getBoundingClientRect();
49627
49628                     combo
49629                         .style('left', (rect.left + 5 - containerRect.left) + 'px')
49630                         .style('width', (rect.width - 10) + 'px')
49631                         .style('top', (rect.height + rect.top - containerRect.top) + 'px');
49632                 }
49633
49634
49635                 // Dispatches an 'accept' event
49636                 // Then hides the combobox.
49637                 function accept(d) {
49638                     _cancelFetch = true;
49639                     var thiz = input.node();
49640
49641                     if (d) {   // user clicked on a suggestion
49642                         utilGetSetValue(input, d.value);    // replace field contents
49643                         utilTriggerEvent(input, 'change');
49644                     }
49645
49646                     // clear (and keep) selection
49647                     var val = utilGetSetValue(input);
49648                     thiz.setSelectionRange(val.length, val.length);
49649
49650                     d = _fetched[val];
49651                     dispatch$1.call('accept', thiz, d, val);
49652                     hide();
49653                 }
49654
49655
49656                 // Dispatches an 'cancel' event
49657                 // Then hides the combobox.
49658                 function cancel() {
49659                     _cancelFetch = true;
49660                     var thiz = input.node();
49661
49662                     // clear (and remove) selection, and replace field contents
49663                     var val = utilGetSetValue(input);
49664                     var start = input.property('selectionStart');
49665                     var end = input.property('selectionEnd');
49666                     val = val.slice(0, start) + val.slice(end);
49667                     utilGetSetValue(input, val);
49668                     thiz.setSelectionRange(val.length, val.length);
49669
49670                     dispatch$1.call('cancel', thiz);
49671                     hide();
49672                 }
49673
49674             };
49675
49676
49677             combobox.canAutocomplete = function(val) {
49678                 if (!arguments.length) return _canAutocomplete;
49679                 _canAutocomplete = val;
49680                 return combobox;
49681             };
49682
49683             combobox.caseSensitive = function(val) {
49684                 if (!arguments.length) return _caseSensitive;
49685                 _caseSensitive = val;
49686                 return combobox;
49687             };
49688
49689             combobox.data = function(val) {
49690                 if (!arguments.length) return _data;
49691                 _data = val;
49692                 return combobox;
49693             };
49694
49695             combobox.fetcher = function(val) {
49696                 if (!arguments.length) return _fetcher;
49697                 _fetcher = val;
49698                 return combobox;
49699             };
49700
49701             combobox.minItems = function(val) {
49702                 if (!arguments.length) return _minItems;
49703                 _minItems = val;
49704                 return combobox;
49705             };
49706
49707             combobox.itemsMouseEnter = function(val) {
49708                 if (!arguments.length) return _mouseEnterHandler;
49709                 _mouseEnterHandler = val;
49710                 return combobox;
49711             };
49712
49713             combobox.itemsMouseLeave = function(val) {
49714                 if (!arguments.length) return _mouseLeaveHandler;
49715                 _mouseLeaveHandler = val;
49716                 return combobox;
49717             };
49718
49719             return utilRebind(combobox, dispatch$1, 'on');
49720         }
49721
49722
49723         uiCombobox.off = function(input, context) {
49724             input
49725                 .on('focus.combo-input', null)
49726                 .on('blur.combo-input', null)
49727                 .on('keydown.combo-input', null)
49728                 .on('keyup.combo-input', null)
49729                 .on('input.combo-input', null)
49730                 .on('mousedown.combo-input', null)
49731                 .on('mouseup.combo-input', null);
49732
49733
49734             context.container()
49735                 .on('scroll.combo-scroll', null);
49736         };
49737
49738         // toggles the visibility of ui elements, using a combination of the
49739         // hide class, which sets display=none, and a d3 transition for opacity.
49740         // this will cause blinking when called repeatedly, so check that the
49741         // value actually changes between calls.
49742         function uiToggle(show, callback) {
49743             return function(selection) {
49744                 selection
49745                     .style('opacity', show ? 0 : 1)
49746                     .classed('hide', false)
49747                     .transition()
49748                     .style('opacity', show ? 1 : 0)
49749                     .on('end', function() {
49750                         select(this)
49751                             .classed('hide', !show)
49752                             .style('opacity', null);
49753                         if (callback) callback.apply(this);
49754                     });
49755             };
49756         }
49757
49758         function uiDisclosure(context, key, expandedDefault) {
49759             var dispatch$1 = dispatch('toggled');
49760             var _expanded;
49761             var _title = utilFunctor('');
49762             var _updatePreference = true;
49763             var _content = function () {};
49764
49765
49766             var disclosure = function(selection) {
49767
49768                 if (_expanded === undefined || _expanded === null) {
49769                     // loading _expanded here allows it to be reset by calling `disclosure.expanded(null)`
49770
49771                     var preference = corePreferences('disclosure.' + key + '.expanded');
49772                     _expanded = preference === null ? !!expandedDefault : (preference === 'true');
49773                 }
49774
49775                 var hideToggle = selection.selectAll('.hide-toggle-' + key)
49776                     .data([0]);
49777
49778                 // enter
49779                 var hideToggleEnter = hideToggle.enter()
49780                     .append('a')
49781                     .attr('href', '#')
49782                     .attr('class', 'hide-toggle hide-toggle-' + key)
49783                     .call(svgIcon('', 'pre-text', 'hide-toggle-icon'));
49784
49785                 hideToggleEnter
49786                     .append('span')
49787                     .attr('class', 'hide-toggle-text');
49788
49789                 // update
49790                 hideToggle = hideToggleEnter
49791                     .merge(hideToggle);
49792
49793                 hideToggle
49794                     .on('click', toggle)
49795                     .classed('expanded', _expanded);
49796
49797                 hideToggle.selectAll('.hide-toggle-text')
49798                     .text(_title());
49799
49800                 hideToggle.selectAll('.hide-toggle-icon')
49801                     .attr('xlink:href', _expanded ? '#iD-icon-down'
49802                         : (_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward'
49803                     );
49804
49805
49806                 var wrap = selection.selectAll('.disclosure-wrap')
49807                     .data([0]);
49808
49809                 // enter/update
49810                 wrap = wrap.enter()
49811                     .append('div')
49812                     .attr('class', 'disclosure-wrap disclosure-wrap-' + key)
49813                     .merge(wrap)
49814                     .classed('hide', !_expanded);
49815
49816                 if (_expanded) {
49817                     wrap
49818                         .call(_content);
49819                 }
49820
49821
49822                 function toggle() {
49823                     event.preventDefault();
49824
49825                     _expanded = !_expanded;
49826
49827                     if (_updatePreference) {
49828                         corePreferences('disclosure.' + key + '.expanded', _expanded);
49829                     }
49830
49831                     hideToggle
49832                         .classed('expanded', _expanded);
49833
49834                     hideToggle.selectAll('.hide-toggle-icon')
49835                         .attr('xlink:href', _expanded ? '#iD-icon-down'
49836                             : (_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward'
49837                         );
49838
49839                     wrap
49840                         .call(uiToggle(_expanded));
49841
49842                     if (_expanded) {
49843                         wrap
49844                             .call(_content);
49845                     }
49846
49847                     dispatch$1.call('toggled', this, _expanded);
49848                 }
49849             };
49850
49851
49852             disclosure.title = function(val) {
49853                 if (!arguments.length) return _title;
49854                 _title = utilFunctor(val);
49855                 return disclosure;
49856             };
49857
49858
49859             disclosure.expanded = function(val) {
49860                 if (!arguments.length) return _expanded;
49861                 _expanded = val;
49862                 return disclosure;
49863             };
49864
49865
49866             disclosure.updatePreference = function(val) {
49867                 if (!arguments.length) return _updatePreference;
49868                 _updatePreference = val;
49869                 return disclosure;
49870             };
49871
49872
49873             disclosure.content = function(val) {
49874                 if (!arguments.length) return _content;
49875                 _content = val;
49876                 return disclosure;
49877             };
49878
49879
49880             return utilRebind(disclosure, dispatch$1, 'on');
49881         }
49882
49883         // A unit of controls or info to be used in a layout, such as within a pane.
49884         // Can be labeled and collapsible.
49885         function uiSection(id, context) {
49886
49887             var _classes = utilFunctor('');
49888             var _shouldDisplay;
49889             var _content;
49890
49891             var _disclosure;
49892             var _title;
49893             var _expandedByDefault = utilFunctor(true);
49894             var _disclosureContent;
49895             var _disclosureExpanded;
49896
49897             var _containerSelection = select(null);
49898
49899             var section = {
49900                 id: id
49901             };
49902
49903             section.classes = function(val) {
49904                 if (!arguments.length) return _classes;
49905                 _classes = utilFunctor(val);
49906                 return section;
49907             };
49908
49909             section.title = function(val) {
49910                 if (!arguments.length) return _title;
49911                 _title = utilFunctor(val);
49912                 return section;
49913             };
49914
49915             section.expandedByDefault = function(val) {
49916                 if (!arguments.length) return _expandedByDefault;
49917                 _expandedByDefault = utilFunctor(val);
49918                 return section;
49919             };
49920
49921             section.shouldDisplay = function(val) {
49922                 if (!arguments.length) return _shouldDisplay;
49923                 _shouldDisplay = utilFunctor(val);
49924                 return section;
49925             };
49926
49927             section.content = function(val) {
49928                 if (!arguments.length) return _content;
49929                 _content = val;
49930                 return section;
49931             };
49932
49933             section.disclosureContent = function(val) {
49934                 if (!arguments.length) return _disclosureContent;
49935                 _disclosureContent = val;
49936                 return section;
49937             };
49938
49939             section.disclosureExpanded = function(val) {
49940                 if (!arguments.length) return _disclosureExpanded;
49941                 _disclosureExpanded = val;
49942                 return section;
49943             };
49944
49945             // may be called multiple times
49946             section.render = function(selection) {
49947
49948                 _containerSelection = selection
49949                     .selectAll('.section-' + id)
49950                     .data([0]);
49951
49952                 var sectionEnter = _containerSelection
49953                     .enter()
49954                     .append('div')
49955                     .attr('class', 'section section-' + id + ' ' + (_classes && _classes() || ''));
49956
49957                 _containerSelection = sectionEnter
49958                     .merge(_containerSelection);
49959
49960                 _containerSelection
49961                     .call(renderContent);
49962             };
49963
49964             section.reRender = function() {
49965                 _containerSelection
49966                     .call(renderContent);
49967             };
49968
49969             section.selection = function() {
49970                 return _containerSelection;
49971             };
49972
49973             section.disclosure = function() {
49974                 return _disclosure;
49975             };
49976
49977             // may be called multiple times
49978             function renderContent(selection) {
49979                 if (_shouldDisplay) {
49980                     var shouldDisplay = _shouldDisplay();
49981                     selection.classed('hide', !shouldDisplay);
49982                     if (!shouldDisplay) {
49983                         selection.html('');
49984                         return;
49985                     }
49986                 }
49987
49988                 if (_disclosureContent) {
49989                     if (!_disclosure) {
49990                         _disclosure = uiDisclosure(context, id.replace(/-/g, '_'), _expandedByDefault())
49991                             .title(_title || '')
49992                             /*.on('toggled', function(expanded) {
49993                                 if (expanded) { selection.node().parentNode.scrollTop += 200; }
49994                             })*/
49995                             .content(_disclosureContent);
49996                     }
49997                     if (_disclosureExpanded !== undefined) {
49998                         _disclosure.expanded(_disclosureExpanded);
49999                         _disclosureExpanded = undefined;
50000                     }
50001                     selection
50002                         .call(_disclosure);
50003
50004                     return;
50005                 }
50006
50007                 if (_content) {
50008                     selection
50009                         .call(_content);
50010                 }
50011             }
50012
50013             return section;
50014         }
50015
50016         // Pass `which` object of the form:
50017         // {
50018         //   key: 'string',     // required
50019         //   value: 'string'    // optional
50020         // }
50021         //   -or-
50022         // {
50023         //   rtype: 'string'    // relation type  (e.g. 'multipolygon')
50024         // }
50025         //   -or-
50026         // {
50027         //   qid: 'string'      // brand wikidata  (e.g. 'Q37158')
50028         // }
50029         //
50030         function uiTagReference(what) {
50031             var wikibase = what.qid ? services.wikidata : services.osmWikibase;
50032             var tagReference = {};
50033
50034             var _button = select(null);
50035             var _body = select(null);
50036             var _loaded;
50037             var _showing;
50038
50039
50040             function load() {
50041                 if (!wikibase) return;
50042
50043                 _button
50044                     .classed('tag-reference-loading', true);
50045
50046                 wikibase.getDocs(what, gotDocs);
50047             }
50048
50049
50050             function gotDocs(err, docs) {
50051                 _body.html('');
50052
50053                 if (!docs || !docs.title) {
50054                     _body
50055                         .append('p')
50056                         .attr('class', 'tag-reference-description')
50057                         .text(_t('inspector.no_documentation_key'));
50058                     done();
50059                     return;
50060                 }
50061
50062                 if (docs.imageURL) {
50063                     _body
50064                         .append('img')
50065                         .attr('class', 'tag-reference-wiki-image')
50066                         .attr('src', docs.imageURL)
50067                         .on('load', function() { done(); })
50068                         .on('error', function() { select(this).remove(); done(); });
50069                 } else {
50070                     done();
50071                 }
50072
50073                 _body
50074                     .append('p')
50075                     .attr('class', 'tag-reference-description')
50076                     .text(docs.description || _t('inspector.no_documentation_key'))
50077                     .append('a')
50078                     .attr('class', 'tag-reference-edit')
50079                     .attr('target', '_blank')
50080                     .attr('tabindex', -1)
50081                     .attr('title', _t('inspector.edit_reference'))
50082                     .attr('href', docs.editURL)
50083                     .call(svgIcon('#iD-icon-edit', 'inline'));
50084
50085                 if (docs.wiki) {
50086                     _body
50087                       .append('a')
50088                       .attr('class', 'tag-reference-link')
50089                       .attr('target', '_blank')
50090                       .attr('tabindex', -1)
50091                       .attr('href', docs.wiki.url)
50092                       .call(svgIcon('#iD-icon-out-link', 'inline'))
50093                       .append('span')
50094                       .text(_t(docs.wiki.text));
50095                 }
50096
50097                 // Add link to info about "good changeset comments" - #2923
50098                 if (what.key === 'comment') {
50099                     _body
50100                         .append('a')
50101                         .attr('class', 'tag-reference-comment-link')
50102                         .attr('target', '_blank')
50103                         .attr('tabindex', -1)
50104                         .call(svgIcon('#iD-icon-out-link', 'inline'))
50105                         .attr('href', _t('commit.about_changeset_comments_link'))
50106                         .append('span')
50107                         .text(_t('commit.about_changeset_comments'));
50108                 }
50109             }
50110
50111
50112             function done() {
50113                 _loaded = true;
50114
50115                 _button
50116                     .classed('tag-reference-loading', false);
50117
50118                 _body
50119                     .classed('expanded', true)
50120                     .transition()
50121                     .duration(200)
50122                     .style('max-height', '200px')
50123                     .style('opacity', '1');
50124
50125                 _showing = true;
50126
50127                 _button.selectAll('svg.icon use').each(function() {
50128                     var iconUse = select(this);
50129                     if (iconUse.attr('href') === '#iD-icon-info') {
50130                         iconUse.attr('href', '#iD-icon-info-filled');
50131                     }
50132                 });
50133             }
50134
50135
50136             function hide() {
50137                 _body
50138                     .transition()
50139                     .duration(200)
50140                     .style('max-height', '0px')
50141                     .style('opacity', '0')
50142                     .on('end', function () {
50143                         _body.classed('expanded', false);
50144                     });
50145
50146                 _showing = false;
50147
50148                 _button.selectAll('svg.icon use').each(function() {
50149                     var iconUse = select(this);
50150                     if (iconUse.attr('href') === '#iD-icon-info-filled') {
50151                         iconUse.attr('href', '#iD-icon-info');
50152                     }
50153                 });
50154
50155             }
50156
50157
50158             tagReference.button = function(selection, klass, iconName) {
50159                 _button = selection.selectAll('.tag-reference-button')
50160                     .data([0]);
50161
50162                 _button = _button.enter()
50163                     .append('button')
50164                     .attr('class', 'tag-reference-button ' + klass)
50165                     .attr('title', _t('icons.information'))
50166                     .attr('tabindex', -1)
50167                     .call(svgIcon('#iD-icon-' + (iconName || 'inspect')))
50168                     .merge(_button);
50169
50170                 _button
50171                     .on('click', function () {
50172                         event.stopPropagation();
50173                         event.preventDefault();
50174                         this.blur();    // avoid keeping focus on the button - #4641
50175                         if (_showing) {
50176                             hide();
50177                         } else if (_loaded) {
50178                             done();
50179                         } else {
50180                             load();
50181                         }
50182                     });
50183             };
50184
50185
50186             tagReference.body = function(selection) {
50187                 var itemID = what.qid || what.rtype || (what.key + '-' + what.value);
50188                 _body = selection.selectAll('.tag-reference-body')
50189                     .data([itemID], function(d) { return d; });
50190
50191                 _body.exit()
50192                     .remove();
50193
50194                 _body = _body.enter()
50195                     .append('div')
50196                     .attr('class', 'tag-reference-body')
50197                     .style('max-height', '0')
50198                     .style('opacity', '0')
50199                     .merge(_body);
50200
50201                 if (_showing === false) {
50202                     hide();
50203                 }
50204             };
50205
50206
50207             tagReference.showing = function(val) {
50208                 if (!arguments.length) return _showing;
50209                 _showing = val;
50210                 return tagReference;
50211             };
50212
50213
50214             return tagReference;
50215         }
50216
50217         function uiSectionRawTagEditor(id, context) {
50218
50219             var section = uiSection(id, context)
50220                 .classes('raw-tag-editor')
50221                 .title(function() {
50222                     var count = Object.keys(_tags).filter(function(d) { return d; }).length;
50223                     return _t('inspector.title_count', { title: _t('inspector.tags'), count: count });
50224                 })
50225                 .expandedByDefault(false)
50226                 .disclosureContent(renderDisclosureContent);
50227
50228             var taginfo = services.taginfo;
50229             var dispatch$1 = dispatch('change');
50230             var availableViews = [
50231                 { id: 'text', icon: '#fas-i-cursor' },
50232                 { id: 'list', icon: '#fas-th-list' }
50233             ];
50234
50235             var _tagView = (corePreferences('raw-tag-editor-view') || 'list');   // 'list, 'text'
50236             var _readOnlyTags = [];
50237             // the keys in the order we want them to display
50238             var _orderedKeys = [];
50239             var _showBlank = false;
50240             var _pendingChange = null;
50241             var _state;
50242             var _presets;
50243             var _tags;
50244             var _entityIDs;
50245
50246             function renderDisclosureContent(wrap) {
50247
50248                 // remove deleted keys
50249                 _orderedKeys = _orderedKeys.filter(function(key) {
50250                     return _tags[key] !== undefined;
50251                 });
50252
50253                 // When switching to a different entity or changing the state (hover/select)
50254                 // reorder the keys alphabetically.
50255                 // We trigger this by emptying the `_orderedKeys` array, then it will be rebuilt here.
50256                 // Otherwise leave their order alone - #5857, #5927
50257                 var all = Object.keys(_tags).sort();
50258                 var missingKeys = utilArrayDifference(all, _orderedKeys);
50259                 for (var i in missingKeys) {
50260                     _orderedKeys.push(missingKeys[i]);
50261                 }
50262
50263                 // assemble row data
50264                 var rowData = _orderedKeys.map(function(key, i) {
50265                     return { index: i, key: key, value: _tags[key] };
50266                 });
50267
50268                 // append blank row last, if necessary
50269                 if (!rowData.length || _showBlank) {
50270                     _showBlank = false;
50271                     rowData.push({ index: rowData.length, key: '', value: '' });
50272                 }
50273
50274
50275                 // View Options
50276                 var options = wrap.selectAll('.raw-tag-options')
50277                     .data([0]);
50278
50279                 options.exit()
50280                     .remove();
50281
50282                 var optionsEnter = options.enter()
50283                     .insert('div', ':first-child')
50284                     .attr('class', 'raw-tag-options');
50285
50286                 var optionEnter = optionsEnter.selectAll('.raw-tag-option')
50287                     .data(availableViews, function(d) { return d.id; })
50288                     .enter();
50289
50290                 optionEnter
50291                     .append('button')
50292                     .attr('class', function(d) {
50293                         return 'raw-tag-option raw-tag-option-' + d.id + (_tagView === d.id ? ' selected' : '');
50294                     })
50295                     .attr('title', function(d) { return _t('icons.' + d.id); })
50296                     .on('click', function(d) {
50297                         _tagView = d.id;
50298                         corePreferences('raw-tag-editor-view', d.id);
50299
50300                         wrap.selectAll('.raw-tag-option')
50301                             .classed('selected', function(datum) { return datum === d; });
50302
50303                         wrap.selectAll('.tag-text')
50304                             .classed('hide', (d.id !== 'text'))
50305                             .each(setTextareaHeight);
50306
50307                         wrap.selectAll('.tag-list, .add-row')
50308                             .classed('hide', (d.id !== 'list'));
50309                     })
50310                     .each(function(d) {
50311                         select(this)
50312                             .call(svgIcon(d.icon));
50313                     });
50314
50315
50316                 // View as Text
50317                 var textData = rowsToText(rowData);
50318                 var textarea = wrap.selectAll('.tag-text')
50319                     .data([0]);
50320
50321                 textarea = textarea.enter()
50322                     .append('textarea')
50323                     .attr('class', 'tag-text' + (_tagView !== 'text' ? ' hide' : ''))
50324                     .call(utilNoAuto)
50325                     .attr('placeholder', _t('inspector.key_value'))
50326                     .attr('spellcheck', 'false')
50327                     .merge(textarea);
50328
50329                 textarea
50330                     .call(utilGetSetValue, textData)
50331                     .each(setTextareaHeight)
50332                     .on('input', setTextareaHeight)
50333                     .on('blur', textChanged)
50334                     .on('change', textChanged);
50335
50336
50337                 // View as List
50338                 var list = wrap.selectAll('.tag-list')
50339                     .data([0]);
50340
50341                 list = list.enter()
50342                     .append('ul')
50343                     .attr('class', 'tag-list' + (_tagView !== 'list' ? ' hide' : ''))
50344                     .merge(list);
50345
50346
50347                 // Container for the Add button
50348                 var addRowEnter = wrap.selectAll('.add-row')
50349                     .data([0])
50350                     .enter()
50351                     .append('div')
50352                     .attr('class', 'add-row' + (_tagView !== 'list' ? ' hide' : ''));
50353
50354                 addRowEnter
50355                     .append('button')
50356                     .attr('class', 'add-tag')
50357                     .call(svgIcon('#iD-icon-plus', 'light'))
50358                     .on('click', addTag);
50359
50360                 addRowEnter
50361                     .append('div')
50362                     .attr('class', 'space-value');   // preserve space
50363
50364                 addRowEnter
50365                     .append('div')
50366                     .attr('class', 'space-buttons');  // preserve space
50367
50368
50369                 // Tag list items
50370                 var items = list.selectAll('.tag-row')
50371                     .data(rowData, function(d) { return d.key; });
50372
50373                 items.exit()
50374                     .each(unbind)
50375                     .remove();
50376
50377
50378                 // Enter
50379                 var itemsEnter = items.enter()
50380                     .append('li')
50381                     .attr('class', 'tag-row')
50382                     .classed('readonly', isReadOnly);
50383
50384                 var innerWrap = itemsEnter.append('div')
50385                     .attr('class', 'inner-wrap');
50386
50387                 innerWrap
50388                     .append('div')
50389                     .attr('class', 'key-wrap')
50390                     .append('input')
50391                     .property('type', 'text')
50392                     .attr('class', 'key')
50393                     .call(utilNoAuto)
50394                     .on('blur', keyChange)
50395                     .on('change', keyChange);
50396
50397                 innerWrap
50398                     .append('div')
50399                     .attr('class', 'value-wrap')
50400                     .append('input')
50401                     .property('type', 'text')
50402                     .attr('class', 'value')
50403                     .call(utilNoAuto)
50404                     .on('blur', valueChange)
50405                     .on('change', valueChange)
50406                     .on('keydown.push-more', pushMore);
50407
50408                 innerWrap
50409                     .append('button')
50410                     .attr('tabindex', -1)
50411                     .attr('class', 'form-field-button remove')
50412                     .attr('title', _t('icons.remove'))
50413                     .call(svgIcon('#iD-operation-delete'));
50414
50415
50416                 // Update
50417                 items = items
50418                     .merge(itemsEnter)
50419                     .sort(function(a, b) { return a.index - b.index; });
50420
50421                 items
50422                     .each(function(d) {
50423                         var row = select(this);
50424                         var key = row.select('input.key');      // propagate bound data
50425                         var value = row.select('input.value');  // propagate bound data
50426
50427                         if (_entityIDs && taginfo && _state !== 'hover') {
50428                             bindTypeahead(key, value);
50429                         }
50430
50431                         var reference;
50432
50433                         if (typeof d.value !== 'string') {
50434                             reference = uiTagReference({ key: d.key });
50435                         } else {
50436                             var isRelation = _entityIDs && _entityIDs.some(function(entityID) {
50437                                 return context.entity(entityID).type === 'relation';
50438                             });
50439                             if (isRelation && d.key === 'type') {
50440                                 reference = uiTagReference({ rtype: d.value });
50441                             } else {
50442                                 reference = uiTagReference({ key: d.key, value: d.value });
50443                             }
50444                         }
50445
50446                         if (_state === 'hover') {
50447                             reference.showing(false);
50448                         }
50449
50450                         row.select('.inner-wrap')      // propagate bound data
50451                             .call(reference.button);
50452
50453                         row.call(reference.body);
50454
50455                         row.select('button.remove');   // propagate bound data
50456                     });
50457
50458                 items.selectAll('input.key')
50459                     .attr('title', function(d) { return d.key; })
50460                     .call(utilGetSetValue, function(d) { return d.key; })
50461                     .attr('readonly', function(d) {
50462                         return (isReadOnly(d) || (typeof d.value !== 'string')) || null;
50463                     });
50464
50465                 items.selectAll('input.value')
50466                     .attr('title', function(d) {
50467                         return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : d.value;
50468                     })
50469                     .classed('mixed', function(d) {
50470                         return Array.isArray(d.value);
50471                     })
50472                     .attr('placeholder', function(d) {
50473                         return typeof d.value === 'string' ? null : _t('inspector.multiple_values');
50474                     })
50475                     .call(utilGetSetValue, function(d) {
50476                         return typeof d.value === 'string' ? d.value : '';
50477                     })
50478                     .attr('readonly', function(d) {
50479                         return isReadOnly(d) || null;
50480                     });
50481
50482                 items.selectAll('button.remove')
50483                     .on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'down', removeTag);  // 'click' fires too late - #5878
50484
50485             }
50486
50487             function isReadOnly(d) {
50488                 for (var i = 0; i < _readOnlyTags.length; i++) {
50489                     if (d.key.match(_readOnlyTags[i]) !== null) {
50490                         return true;
50491                     }
50492                 }
50493                 return false;
50494             }
50495
50496             function setTextareaHeight() {
50497                 if (_tagView !== 'text') return;
50498
50499                 var selection = select(this);
50500                 selection.style('height', null);
50501                 selection.style('height', selection.node().scrollHeight + 5 + 'px');
50502             }
50503
50504             function stringify(s) {
50505                 return JSON.stringify(s).slice(1, -1);   // without leading/trailing "
50506             }
50507
50508             function unstringify(s) {
50509                 var leading = '';
50510                 var trailing = '';
50511                 if (s.length < 1 || s.charAt(0) !== '"') {
50512                     leading = '"';
50513                 }
50514                 if (s.length < 2 || s.charAt(s.length - 1) !== '"' ||
50515                     (s.charAt(s.length - 1) === '"' && s.charAt(s.length - 2) === '\\')
50516                 ) {
50517                     trailing = '"';
50518                 }
50519                 return JSON.parse(leading + s + trailing);
50520             }
50521
50522             function rowsToText(rows) {
50523                 var str = rows
50524                     .filter(function(row) { return row.key && row.key.trim() !== ''; })
50525                     .map(function(row) {
50526                         var rawVal = row.value;
50527                         if (typeof rawVal !== 'string') rawVal = '*';
50528                         var val = rawVal ? stringify(rawVal) : '';
50529                         return stringify(row.key) + '=' + val;
50530                     })
50531                     .join('\n');
50532
50533                 if (_state !== 'hover' && str.length) {
50534                     return str + '\n';
50535                 }
50536                 return  str;
50537             }
50538
50539             function textChanged() {
50540                 var newText = this.value.trim();
50541                 var newTags = {};
50542                 newText.split('\n').forEach(function(row) {
50543                     var m = row.match(/^\s*([^=]+)=(.*)$/);
50544                     if (m !== null) {
50545                         var k = context.cleanTagKey(unstringify(m[1].trim()));
50546                         var v = context.cleanTagValue(unstringify(m[2].trim()));
50547                         newTags[k] = v;
50548                     }
50549                 });
50550
50551                 var tagDiff = utilTagDiff(_tags, newTags);
50552                 if (!tagDiff.length) return;
50553
50554                 _pendingChange  = _pendingChange || {};
50555
50556                 tagDiff.forEach(function(change) {
50557                     if (isReadOnly({ key: change.key })) return;
50558
50559                     // skip unchanged multiselection placeholders
50560                     if (change.newVal === '*' && typeof change.oldVal !== 'string') return;
50561
50562                     if (change.type === '-') {
50563                         _pendingChange[change.key] = undefined;
50564                     } else if (change.type === '+') {
50565                         _pendingChange[change.key] = change.newVal || '';
50566                     }
50567                 });
50568
50569                 if (Object.keys(_pendingChange).length === 0) {
50570                     _pendingChange = null;
50571                     return;
50572                 }
50573
50574                 scheduleChange();
50575             }
50576
50577             function pushMore() {
50578                 // if pressing Tab on the last value field with content, add a blank row
50579                 if (event.keyCode === 9 && !event.shiftKey &&
50580                     section.selection().selectAll('.tag-list li:last-child input.value').node() === this &&
50581                     utilGetSetValue(select(this))) {
50582                     addTag();
50583                 }
50584             }
50585
50586             function bindTypeahead(key, value) {
50587                 if (isReadOnly(key.datum())) return;
50588
50589                 if (Array.isArray(value.datum().value)) {
50590                     value.call(uiCombobox(context, 'tag-value')
50591                         .minItems(1)
50592                         .fetcher(function(value, callback) {
50593                             var keyString = utilGetSetValue(key);
50594                             if (!_tags[keyString]) return;
50595                             var data = _tags[keyString].filter(Boolean).map(function(tagValue) {
50596                                 return {
50597                                     value: tagValue,
50598                                     title: tagValue
50599                                 };
50600                             });
50601                             callback(data);
50602                         }));
50603                     return;
50604                 }
50605
50606                 var geometry = context.graph().geometry(_entityIDs[0]);
50607
50608                 key.call(uiCombobox(context, 'tag-key')
50609                     .fetcher(function(value, callback) {
50610                         taginfo.keys({
50611                             debounce: true,
50612                             geometry: geometry,
50613                             query: value
50614                         }, function(err, data) {
50615                             if (!err) {
50616                                 var filtered = data.filter(function(d) { return _tags[d.value] === undefined; });
50617                                 callback(sort(value, filtered));
50618                             }
50619                         });
50620                     }));
50621
50622                 value.call(uiCombobox(context, 'tag-value')
50623                     .fetcher(function(value, callback) {
50624                         taginfo.values({
50625                             debounce: true,
50626                             key: utilGetSetValue(key),
50627                             geometry: geometry,
50628                             query: value
50629                         }, function(err, data) {
50630                             if (!err) callback(sort(value, data));
50631                         });
50632                     }));
50633
50634
50635                 function sort(value, data) {
50636                     var sameletter = [];
50637                     var other = [];
50638                     for (var i = 0; i < data.length; i++) {
50639                         if (data[i].value.substring(0, value.length) === value) {
50640                             sameletter.push(data[i]);
50641                         } else {
50642                             other.push(data[i]);
50643                         }
50644                     }
50645                     return sameletter.concat(other);
50646                 }
50647             }
50648
50649             function unbind() {
50650                 var row = select(this);
50651
50652                 row.selectAll('input.key')
50653                     .call(uiCombobox.off, context);
50654
50655                 row.selectAll('input.value')
50656                     .call(uiCombobox.off, context);
50657             }
50658
50659             function keyChange(d) {
50660                 if (select(this).attr('readonly')) return;
50661
50662                 var kOld = d.key;
50663
50664                 // exit if we are currently about to delete this row anyway - #6366
50665                 if (_pendingChange && _pendingChange.hasOwnProperty(kOld) && _pendingChange[kOld] === undefined) return;
50666
50667                 var kNew = context.cleanTagKey(this.value.trim());
50668
50669                 // allow no change if the key should be readonly
50670                 if (isReadOnly({ key: kNew })) {
50671                     this.value = kOld;
50672                     return;
50673                 }
50674
50675                 if (kNew &&
50676                     kNew !== kOld &&
50677                     _tags[kNew] !== undefined) {
50678                     // new key is already in use, switch focus to the existing row
50679
50680                     this.value = kOld;                // reset the key
50681                     section.selection().selectAll('.tag-list input.value')
50682                         .each(function(d) {
50683                             if (d.key === kNew) {     // send focus to that other value combo instead
50684                                 var input = select(this).node();
50685                                 input.focus();
50686                                 input.select();
50687                             }
50688                         });
50689                     return;
50690                 }
50691
50692
50693                 var row = this.parentNode.parentNode;
50694                 var inputVal = select(row).selectAll('input.value');
50695                 var vNew = context.cleanTagValue(utilGetSetValue(inputVal));
50696
50697                 _pendingChange = _pendingChange || {};
50698
50699                 if (kOld) {
50700                     _pendingChange[kOld] = undefined;
50701                 }
50702
50703                 _pendingChange[kNew] = vNew;
50704
50705                 // update the ordered key index so this row doesn't change position
50706                 var existingKeyIndex = _orderedKeys.indexOf(kOld);
50707                 if (existingKeyIndex !== -1) _orderedKeys[existingKeyIndex] = kNew;
50708
50709                 d.key = kNew;    // update datum to avoid exit/enter on tag update
50710                 d.value = vNew;
50711
50712                 this.value = kNew;
50713                 utilGetSetValue(inputVal, vNew);
50714                 scheduleChange();
50715             }
50716
50717             function valueChange(d) {
50718                 if (isReadOnly(d)) return;
50719
50720                 // exit if this is a multiselection and no value was entered
50721                 if (typeof d.value !== 'string' && !this.value) return;
50722
50723                 // exit if we are currently about to delete this row anyway - #6366
50724                 if (_pendingChange && _pendingChange.hasOwnProperty(d.key) && _pendingChange[d.key] === undefined) return;
50725
50726                 _pendingChange = _pendingChange || {};
50727
50728                 _pendingChange[d.key] = context.cleanTagValue(this.value);
50729                 scheduleChange();
50730             }
50731
50732             function removeTag(d) {
50733                 if (isReadOnly(d)) return;
50734
50735                 if (d.key === '') {    // removing the blank row
50736                     _showBlank = false;
50737                     section.reRender();
50738
50739                 } else {
50740                     // remove the key from the ordered key index
50741                     _orderedKeys = _orderedKeys.filter(function(key) { return key !== d.key; });
50742
50743                     _pendingChange  = _pendingChange || {};
50744                     _pendingChange[d.key] = undefined;
50745                     scheduleChange();
50746                 }
50747             }
50748
50749             function addTag() {
50750                 // Delay render in case this click is blurring an edited combo.
50751                 // Without the setTimeout, the `content` render would wipe out the pending tag change.
50752                 window.setTimeout(function() {
50753                     _showBlank = true;
50754                     section.reRender();
50755                     section.selection().selectAll('.tag-list li:last-child input.key').node().focus();
50756                 }, 20);
50757             }
50758
50759             function scheduleChange() {
50760                 // Cache IDs in case the editor is reloaded before the change event is called. - #6028
50761                 var entityIDs = _entityIDs;
50762
50763                 // Delay change in case this change is blurring an edited combo. - #5878
50764                 window.setTimeout(function() {
50765                     if (!_pendingChange) return;
50766
50767                     dispatch$1.call('change', this, entityIDs, _pendingChange);
50768                     _pendingChange = null;
50769                 }, 10);
50770             }
50771
50772
50773             section.state = function(val) {
50774                 if (!arguments.length) return _state;
50775                 if (_state !== val) {
50776                     _orderedKeys = [];
50777                     _state = val;
50778                 }
50779                 return section;
50780             };
50781
50782
50783             section.presets = function(val) {
50784                 if (!arguments.length) return _presets;
50785                 _presets = val;
50786                 if (_presets && _presets.length && _presets[0].isFallback()) {
50787                     section.disclosureExpanded(true);
50788                 } else {
50789                     section.disclosureExpanded(null);
50790                 }
50791                 return section;
50792             };
50793
50794
50795             section.tags = function(val) {
50796                 if (!arguments.length) return _tags;
50797                 _tags = val;
50798                 return section;
50799             };
50800
50801
50802             section.entityIDs = function(val) {
50803                 if (!arguments.length) return _entityIDs;
50804                 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
50805                     _entityIDs = val;
50806                     _orderedKeys = [];
50807                 }
50808                 return section;
50809             };
50810
50811
50812             // pass an array of regular expressions to test against the tag key
50813             section.readOnlyTags = function(val) {
50814                 if (!arguments.length) return _readOnlyTags;
50815                 _readOnlyTags = val;
50816                 return section;
50817             };
50818
50819
50820             return utilRebind(section, dispatch$1, 'on');
50821         }
50822
50823         function uiDataEditor(context) {
50824             var dataHeader = uiDataHeader();
50825             var rawTagEditor = uiSectionRawTagEditor('custom-data-tag-editor', context)
50826                 .expandedByDefault(true)
50827                 .readOnlyTags([/./]);
50828             var _datum;
50829
50830
50831             function dataEditor(selection) {
50832
50833                 var header = selection.selectAll('.header')
50834                     .data([0]);
50835
50836                 var headerEnter = header.enter()
50837                     .append('div')
50838                     .attr('class', 'header fillL');
50839
50840                 headerEnter
50841                     .append('button')
50842                     .attr('class', 'close')
50843                     .on('click', function() {
50844                         context.enter(modeBrowse(context));
50845                     })
50846                     .call(svgIcon('#iD-icon-close'));
50847
50848                 headerEnter
50849                     .append('h3')
50850                     .text(_t('map_data.title'));
50851
50852
50853                 var body = selection.selectAll('.body')
50854                     .data([0]);
50855
50856                 body = body.enter()
50857                     .append('div')
50858                     .attr('class', 'body')
50859                     .merge(body);
50860
50861                 var editor = body.selectAll('.data-editor')
50862                     .data([0]);
50863
50864                 // enter/update
50865                 editor.enter()
50866                     .append('div')
50867                     .attr('class', 'modal-section data-editor')
50868                     .merge(editor)
50869                     .call(dataHeader.datum(_datum));
50870
50871                 var rte = body.selectAll('.raw-tag-editor')
50872                     .data([0]);
50873
50874                 // enter/update
50875                 rte.enter()
50876                     .append('div')
50877                     .attr('class', 'raw-tag-editor data-editor')
50878                     .merge(rte)
50879                     .call(rawTagEditor
50880                         .tags((_datum && _datum.properties) || {})
50881                         .state('hover')
50882                         .render
50883                     )
50884                     .selectAll('textarea.tag-text')
50885                     .attr('readonly', true)
50886                     .classed('readonly', true);
50887             }
50888
50889
50890             dataEditor.datum = function(val) {
50891                 if (!arguments.length) return _datum;
50892                 _datum = val;
50893                 return this;
50894             };
50895
50896
50897             return dataEditor;
50898         }
50899
50900         function modeSelectData(context, selectedDatum) {
50901             var mode = {
50902                 id: 'select-data',
50903                 button: 'browse'
50904             };
50905
50906             var keybinding = utilKeybinding('select-data');
50907             var dataEditor = uiDataEditor(context);
50908
50909             var behaviors = [
50910                 behaviorBreathe(),
50911                 behaviorHover(context),
50912                 behaviorSelect(context),
50913                 behaviorLasso(context),
50914                 modeDragNode(context).behavior,
50915                 modeDragNote(context).behavior
50916             ];
50917
50918
50919             // class the data as selected, or return to browse mode if the data is gone
50920             function selectData(drawn) {
50921                 var selection = context.surface().selectAll('.layer-mapdata .data' + selectedDatum.__featurehash__);
50922
50923                 if (selection.empty()) {
50924                     // Return to browse mode if selected DOM elements have
50925                     // disappeared because the user moved them out of view..
50926                     var source = event && event.type === 'zoom' && event.sourceEvent;
50927                     if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
50928                         context.enter(modeBrowse(context));
50929                     }
50930                 } else {
50931                     selection.classed('selected', true);
50932                 }
50933             }
50934
50935
50936             function esc() {
50937                 if (context.container().select('.combobox').size()) return;
50938                 context.enter(modeBrowse(context));
50939             }
50940
50941
50942             mode.zoomToSelected = function() {
50943                 var extent = geoExtent(d3_geoBounds(selectedDatum));
50944                 context.map().centerZoomEase(extent.center(), context.map().trimmedExtentZoom(extent));
50945             };
50946
50947
50948             mode.enter = function() {
50949                 behaviors.forEach(context.install);
50950
50951                 keybinding
50952                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
50953                     .on('⎋', esc, true);
50954
50955                 select(document)
50956                     .call(keybinding);
50957
50958                 selectData();
50959
50960                 var sidebar = context.ui().sidebar;
50961                 sidebar.show(dataEditor.datum(selectedDatum));
50962
50963                 // expand the sidebar, avoid obscuring the data if needed
50964                 var extent = geoExtent(d3_geoBounds(selectedDatum));
50965                 sidebar.expand(sidebar.intersects(extent));
50966
50967                 context.map()
50968                     .on('drawn.select-data', selectData);
50969             };
50970
50971
50972             mode.exit = function() {
50973                 behaviors.forEach(context.uninstall);
50974
50975                 select(document)
50976                     .call(keybinding.unbind);
50977
50978                 context.surface()
50979                     .selectAll('.layer-mapdata .selected')
50980                     .classed('selected hover', false);
50981
50982                 context.map()
50983                     .on('drawn.select-data', null);
50984
50985                 context.ui().sidebar
50986                     .hide();
50987             };
50988
50989
50990             return mode;
50991         }
50992
50993         function uiImproveOsmComments() {
50994           let _qaItem;
50995
50996           function issueComments(selection) {
50997             // make the div immediately so it appears above the buttons
50998             let comments = selection.selectAll('.comments-container')
50999               .data([0]);
51000
51001             comments = comments.enter()
51002               .append('div')
51003                 .attr('class', 'comments-container')
51004               .merge(comments);
51005
51006             // must retrieve comments from API before they can be displayed
51007             services.improveOSM.getComments(_qaItem)
51008               .then(d => {
51009                 if (!d.comments) return; // nothing to do here
51010
51011                 const commentEnter = comments.selectAll('.comment')
51012                   .data(d.comments)
51013                   .enter()
51014                   .append('div')
51015                     .attr('class', 'comment');
51016
51017                 commentEnter
51018                   .append('div')
51019                     .attr('class', 'comment-avatar')
51020                     .call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
51021
51022                 const mainEnter = commentEnter
51023                   .append('div')
51024                   .attr('class', 'comment-main');
51025
51026                 const metadataEnter = mainEnter
51027                   .append('div')
51028                     .attr('class', 'comment-metadata');
51029
51030                 metadataEnter
51031                   .append('div')
51032                     .attr('class', 'comment-author')
51033                     .each(function(d) {
51034                       const osm = services.osm;
51035                       let selection = select(this);
51036                       if (osm && d.username) {
51037                         selection = selection
51038                           .append('a')
51039                           .attr('class', 'comment-author-link')
51040                           .attr('href', osm.userURL(d.username))
51041                           .attr('tabindex', -1)
51042                           .attr('target', '_blank');
51043                       }
51044                       selection
51045                         .text(d => d.username);
51046                     });
51047
51048                 metadataEnter
51049                   .append('div')
51050                     .attr('class', 'comment-date')
51051                     .text(d => _t('note.status.commented', { when: localeDateString(d.timestamp) }));
51052
51053                 mainEnter
51054                   .append('div')
51055                     .attr('class', 'comment-text')
51056                   .append('p')
51057                     .text(d => d.text);
51058             })
51059             .catch(err => {
51060               console.log(err); // eslint-disable-line no-console
51061             });
51062           }
51063
51064           function localeDateString(s) {
51065             if (!s) return null;
51066             const options = { day: 'numeric', month: 'short', year: 'numeric' };
51067             const d = new Date(s * 1000); // timestamp is served in seconds, date takes ms
51068             if (isNaN(d.getTime())) return null;
51069             return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
51070           }
51071
51072           issueComments.issue = function(val) {
51073             if (!arguments.length) return _qaItem;
51074             _qaItem = val;
51075             return issueComments;
51076           };
51077
51078           return issueComments;
51079         }
51080
51081         function uiImproveOsmDetails(context) {
51082           let _qaItem;
51083
51084
51085           function issueDetail(d) {
51086             if (d.desc) return d.desc;
51087             const issueKey = d.issueKey;
51088             d.replacements = d.replacements || {};
51089             d.replacements.default = _t('inspector.unknown');  // special key `default` works as a fallback string
51090             return _t(`QA.improveOSM.error_types.${issueKey}.description`, d.replacements);
51091           }
51092
51093
51094           function improveOsmDetails(selection) {
51095             const details = selection.selectAll('.error-details')
51096               .data(
51097                 (_qaItem ? [_qaItem] : []),
51098                 d => `${d.id}-${d.status || 0}`
51099               );
51100
51101             details.exit()
51102               .remove();
51103
51104             const detailsEnter = details.enter()
51105               .append('div')
51106                 .attr('class', 'error-details qa-details-container');
51107
51108
51109             // description
51110             const descriptionEnter = detailsEnter
51111               .append('div')
51112                 .attr('class', 'qa-details-subsection');
51113
51114             descriptionEnter
51115               .append('h4')
51116                 .text(() => _t('QA.keepRight.detail_description'));
51117
51118             descriptionEnter
51119               .append('div')
51120                 .attr('class', 'qa-details-description-text')
51121                 .html(issueDetail);
51122
51123             // If there are entity links in the error message..
51124             let relatedEntities = [];
51125             descriptionEnter.selectAll('.error_entity_link, .error_object_link')
51126               .each(function() {
51127                 const link = select(this);
51128                 const isObjectLink = link.classed('error_object_link');
51129                 const entityID = isObjectLink ?
51130                   (utilEntityRoot(_qaItem.objectType) + _qaItem.objectId)
51131                   : this.textContent;
51132                 const entity = context.hasEntity(entityID);
51133
51134                 relatedEntities.push(entityID);
51135
51136                 // Add click handler
51137                 link
51138                   .on('mouseenter', () => {
51139                     utilHighlightEntities([entityID], true, context);
51140                   })
51141                   .on('mouseleave', () => {
51142                     utilHighlightEntities([entityID], false, context);
51143                   })
51144                   .on('click', () => {
51145                     event.preventDefault();
51146
51147                     utilHighlightEntities([entityID], false, context);
51148
51149                     const osmlayer = context.layers().layer('osm');
51150                     if (!osmlayer.enabled()) {
51151                       osmlayer.enabled(true);
51152                     }
51153
51154                     context.map().centerZoom(_qaItem.loc, 20);
51155
51156                     if (entity) {
51157                       context.enter(modeSelect(context, [entityID]));
51158                     } else {
51159                       context.loadEntity(entityID, () => {
51160                         context.enter(modeSelect(context, [entityID]));
51161                       });
51162                     }
51163                   });
51164
51165                 // Replace with friendly name if possible
51166                 // (The entity may not yet be loaded into the graph)
51167                 if (entity) {
51168                   let name = utilDisplayName(entity);  // try to use common name
51169
51170                   if (!name && !isObjectLink) {
51171                     const preset = _mainPresetIndex.match(entity, context.graph());
51172                     name = preset && !preset.isFallback() && preset.name();  // fallback to preset name
51173                   }
51174
51175                   if (name) {
51176                     this.innerText = name;
51177                   }
51178                 }
51179               });
51180
51181             // Don't hide entities related to this error - #5880
51182             context.features().forceVisible(relatedEntities);
51183             context.map().pan([0,0]);  // trigger a redraw
51184           }
51185
51186           improveOsmDetails.issue = function(val) {
51187             if (!arguments.length) return _qaItem;
51188             _qaItem = val;
51189             return improveOsmDetails;
51190           };
51191
51192           return improveOsmDetails;
51193         }
51194
51195         function uiImproveOsmHeader() {
51196           let _qaItem;
51197
51198
51199           function issueTitle(d) {
51200             const issueKey = d.issueKey;
51201             d.replacements = d.replacements || {};
51202             d.replacements.default = _t('inspector.unknown');  // special key `default` works as a fallback string
51203             return _t(`QA.improveOSM.error_types.${issueKey}.title`, d.replacements);
51204           }
51205
51206
51207           function improveOsmHeader(selection) {
51208             const header = selection.selectAll('.qa-header')
51209               .data(
51210                 (_qaItem ? [_qaItem] : []),
51211                 d => `${d.id}-${d.status || 0}`
51212               );
51213
51214             header.exit()
51215               .remove();
51216
51217             const headerEnter = header.enter()
51218               .append('div')
51219                 .attr('class', 'qa-header');
51220
51221             const svgEnter = headerEnter
51222               .append('div')
51223                 .attr('class', 'qa-header-icon')
51224                 .classed('new', d => d.id < 0)
51225               .append('svg')
51226                 .attr('width', '20px')
51227                 .attr('height', '30px')
51228                 .attr('viewbox', '0 0 20 30')
51229                 .attr('class', d => `preset-icon-28 qaItem ${d.service} itemId-${d.id} itemType-${d.itemType}`);
51230
51231             svgEnter
51232               .append('polygon')
51233                 .attr('fill', 'currentColor')
51234                 .attr('class', 'qaItem-fill')
51235                 .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');
51236
51237             svgEnter
51238               .append('use')
51239                 .attr('class', 'icon-annotation')
51240                 .attr('width', '13px')
51241                 .attr('height', '13px')
51242                 .attr('transform', 'translate(3.5, 5)')
51243                 .attr('xlink:href', d => {
51244                   const picon = d.icon;
51245                   if (!picon) {
51246                     return '';
51247                   } else {
51248                     const isMaki = /^maki-/.test(picon);
51249                     return `#${picon}${isMaki ? '-11' : ''}`;
51250                   }
51251                 });
51252
51253             headerEnter
51254               .append('div')
51255                 .attr('class', 'qa-header-label')
51256                 .text(issueTitle);
51257           }
51258
51259           improveOsmHeader.issue = function(val) {
51260             if (!arguments.length) return _qaItem;
51261             _qaItem = val;
51262             return improveOsmHeader;
51263           };
51264
51265           return improveOsmHeader;
51266         }
51267
51268         function uiImproveOsmEditor(context) {
51269           const dispatch$1 = dispatch('change');
51270           const qaDetails = uiImproveOsmDetails(context);
51271           const qaComments = uiImproveOsmComments();
51272           const qaHeader = uiImproveOsmHeader();
51273
51274           let _qaItem;
51275
51276           function improveOsmEditor(selection) {
51277
51278             const headerEnter = selection.selectAll('.header')
51279               .data([0])
51280               .enter()
51281               .append('div')
51282                 .attr('class', 'header fillL');
51283
51284             headerEnter
51285               .append('button')
51286                 .attr('class', 'close')
51287                 .on('click', () => context.enter(modeBrowse(context)))
51288                 .call(svgIcon('#iD-icon-close'));
51289
51290             headerEnter
51291               .append('h3')
51292                 .text(_t('QA.improveOSM.title'));
51293
51294             let body = selection.selectAll('.body')
51295               .data([0]);
51296
51297             body = body.enter()
51298               .append('div')
51299                 .attr('class', 'body')
51300               .merge(body);
51301
51302             const editor = body.selectAll('.qa-editor')
51303               .data([0]);
51304
51305             editor.enter()
51306               .append('div')
51307                 .attr('class', 'modal-section qa-editor')
51308               .merge(editor)
51309                 .call(qaHeader.issue(_qaItem))
51310                 .call(qaDetails.issue(_qaItem))
51311                 .call(qaComments.issue(_qaItem))
51312                 .call(improveOsmSaveSection);
51313           }
51314
51315           function improveOsmSaveSection(selection) {
51316             const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51317             const isShown = (_qaItem && (isSelected || _qaItem.newComment || _qaItem.comment));
51318             let saveSection = selection.selectAll('.qa-save')
51319               .data(
51320                 (isShown ? [_qaItem] : []),
51321                 d => `${d.id}-${d.status || 0}`
51322               );
51323
51324             // exit
51325             saveSection.exit()
51326               .remove();
51327
51328             // enter
51329             const saveSectionEnter = saveSection.enter()
51330               .append('div')
51331                 .attr('class', 'qa-save save-section cf');
51332
51333             saveSectionEnter
51334               .append('h4')
51335                 .attr('class', '.qa-save-header')
51336                 .text(_t('note.newComment'));
51337
51338             saveSectionEnter
51339               .append('textarea')
51340                 .attr('class', 'new-comment-input')
51341                 .attr('placeholder', _t('QA.keepRight.comment_placeholder'))
51342                 .attr('maxlength', 1000)
51343                 .property('value', d => d.newComment)
51344                 .call(utilNoAuto)
51345                 .on('input', changeInput)
51346                 .on('blur', changeInput);
51347
51348             // update
51349             saveSection = saveSectionEnter
51350               .merge(saveSection)
51351                 .call(qaSaveButtons);
51352
51353             function changeInput() {
51354               const input = select(this);
51355               let val = input.property('value').trim();
51356
51357               if (val === '') {
51358                 val = undefined;
51359               }
51360
51361               // store the unsaved comment with the issue itself
51362               _qaItem = _qaItem.update({ newComment: val });
51363
51364               const qaService = services.improveOSM;
51365               if (qaService) {
51366                 qaService.replaceItem(_qaItem);
51367               }
51368
51369               saveSection
51370                 .call(qaSaveButtons);
51371             }
51372           }
51373
51374           function qaSaveButtons(selection) {
51375             const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51376             let buttonSection = selection.selectAll('.buttons')
51377               .data((isSelected ? [_qaItem] : []), d => d.status + d.id);
51378
51379             // exit
51380             buttonSection.exit()
51381               .remove();
51382
51383             // enter
51384             const buttonEnter = buttonSection.enter()
51385               .append('div')
51386                 .attr('class', 'buttons');
51387
51388             buttonEnter
51389               .append('button')
51390                 .attr('class', 'button comment-button action')
51391                 .text(_t('QA.keepRight.save_comment'));
51392
51393             buttonEnter
51394               .append('button')
51395                 .attr('class', 'button close-button action');
51396
51397             buttonEnter
51398               .append('button')
51399                 .attr('class', 'button ignore-button action');
51400
51401             // update
51402             buttonSection = buttonSection
51403               .merge(buttonEnter);
51404
51405             buttonSection.select('.comment-button')
51406               .attr('disabled', d => d.newComment ? null : true)
51407               .on('click.comment', function(d) {
51408                 this.blur();    // avoid keeping focus on the button - #4641
51409                 const qaService = services.improveOSM;
51410                 if (qaService) {
51411                   qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item));
51412                 }
51413               });
51414
51415             buttonSection.select('.close-button')
51416               .text(d => {
51417                 const andComment = (d.newComment ? '_comment' : '');
51418                 return _t(`QA.keepRight.close${andComment}`);
51419               })
51420               .on('click.close', function(d) {
51421                 this.blur();    // avoid keeping focus on the button - #4641
51422                 const qaService = services.improveOSM;
51423                 if (qaService) {
51424                   d.newStatus = 'SOLVED';
51425                   qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item));
51426                 }
51427               });
51428
51429             buttonSection.select('.ignore-button')
51430               .text(d => {
51431                 const andComment = (d.newComment ? '_comment' : '');
51432                 return _t(`QA.keepRight.ignore${andComment}`);
51433               })
51434               .on('click.ignore', function(d) {
51435                 this.blur();    // avoid keeping focus on the button - #4641
51436                 const qaService = services.improveOSM;
51437                 if (qaService) {
51438                   d.newStatus = 'INVALID';
51439                   qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item));
51440                 }
51441               });
51442           }
51443
51444           // NOTE: Don't change method name until UI v3 is merged
51445           improveOsmEditor.error = function(val) {
51446             if (!arguments.length) return _qaItem;
51447             _qaItem = val;
51448             return improveOsmEditor;
51449           };
51450
51451           return utilRebind(improveOsmEditor, dispatch$1, 'on');
51452         }
51453
51454         function uiKeepRightDetails(context) {
51455           let _qaItem;
51456
51457
51458           function issueDetail(d) {
51459             const { itemType, parentIssueType } = d;
51460             const unknown = _t('inspector.unknown');
51461             let replacements = d.replacements || {};
51462             replacements.default = unknown;  // special key `default` works as a fallback string
51463
51464             let detail = _t(`QA.keepRight.errorTypes.${itemType}.description`, replacements);
51465             if (detail === unknown) {
51466               detail = _t(`QA.keepRight.errorTypes.${parentIssueType}.description`, replacements);
51467             }
51468             return detail;
51469           }
51470
51471
51472           function keepRightDetails(selection) {
51473             const details = selection.selectAll('.error-details')
51474               .data(
51475                 (_qaItem ? [_qaItem] : []),
51476                 d => `${d.id}-${d.status || 0}`
51477               );
51478
51479             details.exit()
51480               .remove();
51481
51482             const detailsEnter = details.enter()
51483               .append('div')
51484                 .attr('class', 'error-details qa-details-container');
51485
51486             // description
51487             const descriptionEnter = detailsEnter
51488               .append('div')
51489                 .attr('class', 'qa-details-subsection');
51490
51491             descriptionEnter
51492               .append('h4')
51493                 .text(() => _t('QA.keepRight.detail_description'));
51494
51495             descriptionEnter
51496               .append('div')
51497                 .attr('class', 'qa-details-description-text')
51498                 .html(issueDetail);
51499
51500             // If there are entity links in the error message..
51501             let relatedEntities = [];
51502             descriptionEnter.selectAll('.error_entity_link, .error_object_link')
51503               .each(function() {
51504                 const link = select(this);
51505                 const isObjectLink = link.classed('error_object_link');
51506                 const entityID = isObjectLink ?
51507                   (utilEntityRoot(_qaItem.objectType) + _qaItem.objectId)
51508                   : this.textContent;
51509                 const entity = context.hasEntity(entityID);
51510
51511                 relatedEntities.push(entityID);
51512
51513                 // Add click handler
51514                 link
51515                   .on('mouseenter', () => {
51516                     utilHighlightEntities([entityID], true, context);
51517                   })
51518                   .on('mouseleave', () => {
51519                     utilHighlightEntities([entityID], false, context);
51520                   })
51521                   .on('click', () => {
51522                     event.preventDefault();
51523
51524                     utilHighlightEntities([entityID], false, context);
51525
51526                     const osmlayer = context.layers().layer('osm');
51527                     if (!osmlayer.enabled()) {
51528                       osmlayer.enabled(true);
51529                     }
51530
51531                     context.map().centerZoomEase(_qaItem.loc, 20);
51532
51533                     if (entity) {
51534                       context.enter(modeSelect(context, [entityID]));
51535                     } else {
51536                       context.loadEntity(entityID, () => {
51537                         context.enter(modeSelect(context, [entityID]));
51538                       });
51539                     }
51540                   });
51541
51542                 // Replace with friendly name if possible
51543                 // (The entity may not yet be loaded into the graph)
51544                 if (entity) {
51545                   let name = utilDisplayName(entity);  // try to use common name
51546
51547                   if (!name && !isObjectLink) {
51548                     const preset = _mainPresetIndex.match(entity, context.graph());
51549                     name = preset && !preset.isFallback() && preset.name();  // fallback to preset name
51550                   }
51551
51552                   if (name) {
51553                     this.innerText = name;
51554                   }
51555                 }
51556               });
51557
51558             // Don't hide entities related to this issue - #5880
51559             context.features().forceVisible(relatedEntities);
51560             context.map().pan([0,0]);  // trigger a redraw
51561           }
51562
51563           keepRightDetails.issue = function(val) {
51564             if (!arguments.length) return _qaItem;
51565             _qaItem = val;
51566             return keepRightDetails;
51567           };
51568
51569           return keepRightDetails;
51570         }
51571
51572         function uiKeepRightHeader() {
51573           let _qaItem;
51574
51575
51576           function issueTitle(d) {
51577             const { itemType, parentIssueType } = d;
51578             const unknown = _t('inspector.unknown');
51579             let replacements = d.replacements || {};
51580             replacements.default = unknown;  // special key `default` works as a fallback string
51581
51582             let title = _t(`QA.keepRight.errorTypes.${itemType}.title`, replacements);
51583             if (title === unknown) {
51584               title = _t(`QA.keepRight.errorTypes.${parentIssueType}.title`, replacements);
51585             }
51586             return title;
51587           }
51588
51589
51590           function keepRightHeader(selection) {
51591             const header = selection.selectAll('.qa-header')
51592               .data(
51593                 (_qaItem ? [_qaItem] : []),
51594                 d => `${d.id}-${d.status || 0}`
51595               );
51596
51597             header.exit()
51598               .remove();
51599
51600             const headerEnter = header.enter()
51601               .append('div')
51602                 .attr('class', 'qa-header');
51603
51604             const iconEnter = headerEnter
51605               .append('div')
51606                 .attr('class', 'qa-header-icon')
51607                 .classed('new', d => d.id < 0);
51608
51609             iconEnter
51610               .append('div')
51611                 .attr('class', d => `preset-icon-28 qaItem ${d.service} itemId-${d.id} itemType-${d.parentIssueType}`)
51612                 .call(svgIcon('#iD-icon-bolt', 'qaItem-fill'));
51613
51614             headerEnter
51615               .append('div')
51616                 .attr('class', 'qa-header-label')
51617                 .text(issueTitle);
51618           }
51619
51620
51621           keepRightHeader.issue = function(val) {
51622             if (!arguments.length) return _qaItem;
51623             _qaItem = val;
51624             return keepRightHeader;
51625           };
51626
51627           return keepRightHeader;
51628         }
51629
51630         function uiViewOnKeepRight() {
51631           let _qaItem;
51632
51633           function viewOnKeepRight(selection) {
51634             let url;
51635             if (services.keepRight && (_qaItem instanceof QAItem)) {
51636               url = services.keepRight.issueURL(_qaItem);
51637             }
51638
51639             const link = selection.selectAll('.view-on-keepRight')
51640               .data(url ? [url] : []);
51641
51642             // exit
51643             link.exit()
51644               .remove();
51645
51646             // enter
51647             const linkEnter = link.enter()
51648               .append('a')
51649                 .attr('class', 'view-on-keepRight')
51650                 .attr('target', '_blank')
51651                 .attr('rel', 'noopener') // security measure
51652                 .attr('href', d => d)
51653                 .call(svgIcon('#iD-icon-out-link', 'inline'));
51654
51655             linkEnter
51656               .append('span')
51657                 .text(_t('inspector.view_on_keepRight'));
51658           }
51659
51660           viewOnKeepRight.what = function(val) {
51661             if (!arguments.length) return _qaItem;
51662             _qaItem = val;
51663             return viewOnKeepRight;
51664           };
51665
51666           return viewOnKeepRight;
51667         }
51668
51669         function uiKeepRightEditor(context) {
51670           const dispatch$1 = dispatch('change');
51671           const qaDetails = uiKeepRightDetails(context);
51672           const qaHeader = uiKeepRightHeader();
51673
51674           let _qaItem;
51675
51676           function keepRightEditor(selection) {
51677
51678             const headerEnter = selection.selectAll('.header')
51679               .data([0])
51680               .enter()
51681               .append('div')
51682                 .attr('class', 'header fillL');
51683
51684             headerEnter
51685               .append('button')
51686                 .attr('class', 'close')
51687                 .on('click', () => context.enter(modeBrowse(context)))
51688                 .call(svgIcon('#iD-icon-close'));
51689
51690             headerEnter
51691               .append('h3')
51692                 .text(_t('QA.keepRight.title'));
51693
51694
51695             let body = selection.selectAll('.body')
51696               .data([0]);
51697
51698             body = body.enter()
51699               .append('div')
51700                 .attr('class', 'body')
51701               .merge(body);
51702
51703             const editor = body.selectAll('.qa-editor')
51704               .data([0]);
51705
51706             editor.enter()
51707               .append('div')
51708                 .attr('class', 'modal-section qa-editor')
51709               .merge(editor)
51710                 .call(qaHeader.issue(_qaItem))
51711                 .call(qaDetails.issue(_qaItem))
51712                 .call(keepRightSaveSection);
51713
51714
51715             const footer = selection.selectAll('.footer')
51716               .data([0]);
51717
51718             footer.enter()
51719               .append('div')
51720               .attr('class', 'footer')
51721               .merge(footer)
51722               .call(uiViewOnKeepRight().what(_qaItem));
51723           }
51724
51725
51726           function keepRightSaveSection(selection) {
51727             const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51728             const isShown = (_qaItem && (isSelected || _qaItem.newComment || _qaItem.comment));
51729             let saveSection = selection.selectAll('.qa-save')
51730               .data(
51731                 (isShown ? [_qaItem] : []),
51732                 d => `${d.id}-${d.status || 0}`
51733               );
51734
51735             // exit
51736             saveSection.exit()
51737               .remove();
51738
51739             // enter
51740             const saveSectionEnter = saveSection.enter()
51741               .append('div')
51742                 .attr('class', 'qa-save save-section cf');
51743
51744             saveSectionEnter
51745               .append('h4')
51746                 .attr('class', '.qa-save-header')
51747                 .text(_t('QA.keepRight.comment'));
51748
51749             saveSectionEnter
51750               .append('textarea')
51751                 .attr('class', 'new-comment-input')
51752                 .attr('placeholder', _t('QA.keepRight.comment_placeholder'))
51753                 .attr('maxlength', 1000)
51754                 .property('value', d => d.newComment || d.comment)
51755                 .call(utilNoAuto)
51756                 .on('input', changeInput)
51757                 .on('blur', changeInput);
51758
51759             // update
51760             saveSection = saveSectionEnter
51761               .merge(saveSection)
51762                 .call(qaSaveButtons);
51763
51764             function changeInput() {
51765               const input = select(this);
51766               let val = input.property('value').trim();
51767
51768               if (val === _qaItem.comment) {
51769                 val = undefined;
51770               }
51771
51772               // store the unsaved comment with the issue itself
51773               _qaItem = _qaItem.update({ newComment: val });
51774
51775               const qaService = services.keepRight;
51776               if (qaService) {
51777                 qaService.replaceItem(_qaItem);  // update keepright cache
51778               }
51779
51780               saveSection
51781                 .call(qaSaveButtons);
51782             }
51783           }
51784
51785
51786           function qaSaveButtons(selection) {
51787             const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51788             let buttonSection = selection.selectAll('.buttons')
51789               .data((isSelected ? [_qaItem] : []), d => d.status + d.id);
51790
51791             // exit
51792             buttonSection.exit()
51793               .remove();
51794
51795             // enter
51796             const buttonEnter = buttonSection.enter()
51797               .append('div')
51798                 .attr('class', 'buttons');
51799
51800             buttonEnter
51801               .append('button')
51802                 .attr('class', 'button comment-button action')
51803                 .text(_t('QA.keepRight.save_comment'));
51804
51805             buttonEnter
51806               .append('button')
51807                 .attr('class', 'button close-button action');
51808
51809             buttonEnter
51810               .append('button')
51811                 .attr('class', 'button ignore-button action');
51812
51813             // update
51814             buttonSection = buttonSection
51815               .merge(buttonEnter);
51816
51817             buttonSection.select('.comment-button')   // select and propagate data
51818               .attr('disabled', d => d.newComment ? null : true)
51819               .on('click.comment', function(d) {
51820                 this.blur();    // avoid keeping focus on the button - #4641
51821                 const qaService = services.keepRight;
51822                 if (qaService) {
51823                   qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item));
51824                 }
51825               });
51826
51827             buttonSection.select('.close-button')   // select and propagate data
51828               .text(d => {
51829                 const andComment = (d.newComment ? '_comment' : '');
51830                 return _t(`QA.keepRight.close${andComment}`);
51831               })
51832               .on('click.close', function(d) {
51833                 this.blur();    // avoid keeping focus on the button - #4641
51834                 const qaService = services.keepRight;
51835                 if (qaService) {
51836                   d.newStatus = 'ignore_t';   // ignore temporarily (item fixed)
51837                   qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item));
51838                 }
51839               });
51840
51841             buttonSection.select('.ignore-button')   // select and propagate data
51842               .text(d => {
51843                 const andComment = (d.newComment ? '_comment' : '');
51844                 return _t(`QA.keepRight.ignore${andComment}`);
51845               })
51846               .on('click.ignore', function(d) {
51847                 this.blur();    // avoid keeping focus on the button - #4641
51848                 const qaService = services.keepRight;
51849                 if (qaService) {
51850                   d.newStatus = 'ignore';   // ignore permanently (false positive)
51851                   qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item));
51852                 }
51853               });
51854           }
51855
51856           // NOTE: Don't change method name until UI v3 is merged
51857           keepRightEditor.error = function(val) {
51858             if (!arguments.length) return _qaItem;
51859             _qaItem = val;
51860             return keepRightEditor;
51861           };
51862
51863           return utilRebind(keepRightEditor, dispatch$1, 'on');
51864         }
51865
51866         function uiOsmoseDetails(context) {
51867           let _qaItem;
51868
51869           function issueString(d, type) {
51870             if (!d) return '';
51871
51872             // Issue strings are cached from Osmose API
51873             const s = services.osmose.getStrings(d.itemType);
51874             return (type in s) ? s[type] : '';
51875           }
51876
51877
51878           function osmoseDetails(selection) {
51879             const details = selection.selectAll('.error-details')
51880               .data(
51881                 _qaItem ? [_qaItem] : [],
51882                 d => `${d.id}-${d.status || 0}`
51883               );
51884
51885             details.exit()
51886               .remove();
51887
51888             const detailsEnter = details.enter()
51889               .append('div')
51890                 .attr('class', 'error-details qa-details-container');
51891
51892
51893             // Description
51894             if (issueString(_qaItem, 'detail')) {
51895               const div = detailsEnter
51896                 .append('div')
51897                   .attr('class', 'qa-details-subsection');
51898
51899               div
51900                 .append('h4')
51901                   .text(() => _t('QA.keepRight.detail_description'));
51902
51903               div
51904                 .append('p')
51905                   .attr('class', 'qa-details-description-text')
51906                   .html(d => issueString(d, 'detail'))
51907                 .selectAll('a')
51908                   .attr('rel', 'noopener')
51909                   .attr('target', '_blank');
51910             }
51911
51912             // Elements (populated later as data is requested)
51913             const detailsDiv = detailsEnter
51914               .append('div')
51915                 .attr('class', 'qa-details-subsection');
51916
51917             const elemsDiv = detailsEnter
51918               .append('div')
51919                 .attr('class', 'qa-details-subsection');
51920
51921             // Suggested Fix (musn't exist for every issue type)
51922             if (issueString(_qaItem, 'fix')) {
51923               const div = detailsEnter
51924                 .append('div')
51925                   .attr('class', 'qa-details-subsection');
51926
51927               div
51928                 .append('h4')
51929                   .text(() => _t('QA.osmose.fix_title'));
51930
51931               div
51932                 .append('p')
51933                   .html(d => issueString(d, 'fix'))
51934                 .selectAll('a')
51935                   .attr('rel', 'noopener')
51936                   .attr('target', '_blank');
51937             }
51938
51939             // Common Pitfalls (musn't exist for every issue type)
51940             if (issueString(_qaItem, 'trap')) {
51941               const div = detailsEnter
51942                 .append('div')
51943                   .attr('class', 'qa-details-subsection');
51944
51945               div
51946                 .append('h4')
51947                   .text(() => _t('QA.osmose.trap_title'));
51948
51949               div
51950                 .append('p')
51951                   .html(d => issueString(d, 'trap'))
51952                 .selectAll('a')
51953                   .attr('rel', 'noopener')
51954                   .attr('target', '_blank');
51955             }
51956
51957             // Save current item to check if UI changed by time request resolves
51958             const thisItem = _qaItem;
51959             services.osmose.loadIssueDetail(_qaItem)
51960               .then(d => {
51961                 // No details to add if there are no associated issue elements
51962                 if (!d.elems || d.elems.length === 0) return;
51963
51964                 // Do nothing if UI has moved on by the time this resolves
51965                 if (
51966                   context.selectedErrorID() !== thisItem.id
51967                   && context.container().selectAll(`.qaItem.osmose.hover.itemId-${thisItem.id}`).empty()
51968                 ) return;
51969
51970                 // Things like keys and values are dynamically added to a subtitle string
51971                 if (d.detail) {
51972                   detailsDiv
51973                     .append('h4')
51974                       .text(() => _t('QA.osmose.detail_title'));
51975
51976                   detailsDiv
51977                     .append('p')
51978                       .html(d => d.detail)
51979                     .selectAll('a')
51980                       .attr('rel', 'noopener')
51981                       .attr('target', '_blank');
51982                 }
51983
51984                 // Create list of linked issue elements
51985                 elemsDiv
51986                   .append('h4')
51987                     .text(() => _t('QA.osmose.elems_title'));
51988
51989                 elemsDiv
51990                   .append('ul').selectAll('li')
51991                   .data(d.elems)
51992                   .enter()
51993                   .append('li')
51994                   .append('a')
51995                     .attr('class', 'error_entity_link')
51996                     .text(d => d)
51997                     .each(function() {
51998                       const link = select(this);
51999                       const entityID = this.textContent;
52000                       const entity = context.hasEntity(entityID);
52001
52002                       // Add click handler
52003                       link
52004                         .on('mouseenter', () => {
52005                           utilHighlightEntities([entityID], true, context);
52006                         })
52007                         .on('mouseleave', () => {
52008                           utilHighlightEntities([entityID], false, context);
52009                         })
52010                         .on('click', () => {
52011                           event.preventDefault();
52012
52013                           utilHighlightEntities([entityID], false, context);
52014
52015                           const osmlayer = context.layers().layer('osm');
52016                           if (!osmlayer.enabled()) {
52017                             osmlayer.enabled(true);
52018                           }
52019
52020                           context.map().centerZoom(d.loc, 20);
52021
52022                           if (entity) {
52023                             context.enter(modeSelect(context, [entityID]));
52024                           } else {
52025                             context.loadEntity(entityID, () => {
52026                               context.enter(modeSelect(context, [entityID]));
52027                             });
52028                           }
52029                         });
52030
52031                       // Replace with friendly name if possible
52032                       // (The entity may not yet be loaded into the graph)
52033                       if (entity) {
52034                         let name = utilDisplayName(entity);  // try to use common name
52035
52036                         if (!name) {
52037                           const preset = _mainPresetIndex.match(entity, context.graph());
52038                           name = preset && !preset.isFallback() && preset.name();  // fallback to preset name
52039                         }
52040
52041                         if (name) {
52042                           this.innerText = name;
52043                         }
52044                       }
52045                     });
52046
52047                 // Don't hide entities related to this issue - #5880
52048                 context.features().forceVisible(d.elems);
52049                 context.map().pan([0,0]);  // trigger a redraw
52050               })
52051               .catch(err => {
52052                 console.log(err); // eslint-disable-line no-console
52053               });
52054           }
52055
52056
52057           osmoseDetails.issue = function(val) {
52058             if (!arguments.length) return _qaItem;
52059             _qaItem = val;
52060             return osmoseDetails;
52061           };
52062
52063
52064           return osmoseDetails;
52065         }
52066
52067         function uiOsmoseHeader() {
52068           let _qaItem;
52069
52070           function issueTitle(d) {
52071             const unknown = _t('inspector.unknown');
52072
52073             if (!d) return unknown;
52074
52075             // Issue titles supplied by Osmose
52076             const s = services.osmose.getStrings(d.itemType);
52077             return ('title' in s) ? s.title : unknown;
52078           }
52079
52080           function osmoseHeader(selection) {
52081             const header = selection.selectAll('.qa-header')
52082               .data(
52083                 (_qaItem ? [_qaItem] : []),
52084                 d => `${d.id}-${d.status || 0}`
52085               );
52086
52087             header.exit()
52088               .remove();
52089
52090             const headerEnter = header.enter()
52091               .append('div')
52092                 .attr('class', 'qa-header');
52093
52094             const svgEnter = headerEnter
52095               .append('div')
52096                 .attr('class', 'qa-header-icon')
52097                 .classed('new', d => d.id < 0)
52098               .append('svg')
52099                 .attr('width', '20px')
52100                 .attr('height', '30px')
52101                 .attr('viewbox', '0 0 20 30')
52102                 .attr('class', d => `preset-icon-28 qaItem ${d.service} itemId-${d.id} itemType-${d.itemType}`);
52103
52104             svgEnter
52105               .append('polygon')
52106                 .attr('fill', d => services.osmose.getColor(d.item))
52107                 .attr('class', 'qaItem-fill')
52108                 .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');
52109
52110             svgEnter
52111               .append('use')
52112                 .attr('class', 'icon-annotation')
52113                 .attr('width', '13px')
52114                 .attr('height', '13px')
52115                 .attr('transform', 'translate(3.5, 5)')
52116                 .attr('xlink:href', d => {
52117                   const picon = d.icon;
52118
52119                   if (!picon) {
52120                     return '';
52121                   } else {
52122                     const isMaki = /^maki-/.test(picon);
52123                     return `#${picon}${isMaki ? '-11' : ''}`;
52124                   }
52125                 });
52126
52127             headerEnter
52128               .append('div')
52129                 .attr('class', 'qa-header-label')
52130                 .text(issueTitle);
52131           }
52132
52133           osmoseHeader.issue = function(val) {
52134             if (!arguments.length) return _qaItem;
52135             _qaItem = val;
52136             return osmoseHeader;
52137           };
52138
52139           return osmoseHeader;
52140         }
52141
52142         function uiViewOnOsmose() {
52143           let _qaItem;
52144
52145           function viewOnOsmose(selection) {
52146             let url;
52147             if (services.osmose && (_qaItem instanceof QAItem)) {
52148               url = services.osmose.itemURL(_qaItem);
52149             }
52150
52151             const link = selection.selectAll('.view-on-osmose')
52152               .data(url ? [url] : []);
52153
52154             // exit
52155             link.exit()
52156               .remove();
52157
52158             // enter
52159             const linkEnter = link.enter()
52160               .append('a')
52161                 .attr('class', 'view-on-osmose')
52162                 .attr('target', '_blank')
52163                 .attr('rel', 'noopener') // security measure
52164                 .attr('href', d => d)
52165                 .call(svgIcon('#iD-icon-out-link', 'inline'));
52166
52167             linkEnter
52168               .append('span')
52169                 .text(_t('inspector.view_on_osmose'));
52170           }
52171
52172           viewOnOsmose.what = function(val) {
52173             if (!arguments.length) return _qaItem;
52174             _qaItem = val;
52175             return viewOnOsmose;
52176           };
52177
52178           return viewOnOsmose;
52179         }
52180
52181         function uiOsmoseEditor(context) {
52182           const dispatch$1 = dispatch('change');
52183           const qaDetails = uiOsmoseDetails(context);
52184           const qaHeader = uiOsmoseHeader();
52185
52186           let _qaItem;
52187
52188           function osmoseEditor(selection) {
52189
52190             const header = selection.selectAll('.header')
52191               .data([0]);
52192
52193             const headerEnter = header.enter()
52194               .append('div')
52195                 .attr('class', 'header fillL');
52196
52197             headerEnter
52198               .append('button')
52199                 .attr('class', 'close')
52200                 .on('click', () => context.enter(modeBrowse(context)))
52201                 .call(svgIcon('#iD-icon-close'));
52202
52203             headerEnter
52204               .append('h3')
52205                 .text(_t('QA.osmose.title'));
52206
52207             let body = selection.selectAll('.body')
52208               .data([0]);
52209
52210             body = body.enter()
52211                 .append('div')
52212                 .attr('class', 'body')
52213               .merge(body);
52214
52215             let editor = body.selectAll('.qa-editor')
52216               .data([0]);
52217
52218             editor.enter()
52219               .append('div')
52220                 .attr('class', 'modal-section qa-editor')
52221               .merge(editor)
52222                 .call(qaHeader.issue(_qaItem))
52223                 .call(qaDetails.issue(_qaItem))
52224                 .call(osmoseSaveSection);
52225
52226             const footer = selection.selectAll('.footer')
52227               .data([0]);
52228
52229             footer.enter()
52230               .append('div')
52231               .attr('class', 'footer')
52232               .merge(footer)
52233               .call(uiViewOnOsmose().what(_qaItem));
52234           }
52235
52236           function osmoseSaveSection(selection) {
52237             const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
52238             const isShown = (_qaItem && isSelected);
52239             let saveSection = selection.selectAll('.qa-save')
52240               .data(
52241                 (isShown ? [_qaItem] : []),
52242                 d => `${d.id}-${d.status || 0}`
52243               );
52244
52245             // exit
52246             saveSection.exit()
52247               .remove();
52248
52249             // enter
52250             const saveSectionEnter = saveSection.enter()
52251               .append('div')
52252                 .attr('class', 'qa-save save-section cf');
52253
52254             // update
52255             saveSection = saveSectionEnter
52256               .merge(saveSection)
52257                 .call(qaSaveButtons);
52258           }
52259
52260           function qaSaveButtons(selection) {
52261             const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
52262             let buttonSection = selection.selectAll('.buttons')
52263               .data((isSelected ? [_qaItem] : []), d => d.status + d.id);
52264
52265             // exit
52266             buttonSection.exit()
52267               .remove();
52268
52269             // enter
52270             const buttonEnter = buttonSection.enter()
52271               .append('div')
52272                 .attr('class', 'buttons');
52273
52274             buttonEnter
52275               .append('button')
52276                 .attr('class', 'button close-button action');
52277
52278             buttonEnter
52279               .append('button')
52280                 .attr('class', 'button ignore-button action');
52281
52282             // update
52283             buttonSection = buttonSection
52284               .merge(buttonEnter);
52285
52286             buttonSection.select('.close-button')
52287               .text(() => _t('QA.keepRight.close'))
52288               .on('click.close', function(d) {
52289                 this.blur();    // avoid keeping focus on the button - #4641
52290                 const qaService = services.osmose;
52291                 if (qaService) {
52292                   d.newStatus = 'done';
52293                   qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item));
52294                 }
52295               });
52296
52297             buttonSection.select('.ignore-button')
52298               .text(() => _t('QA.keepRight.ignore'))
52299               .on('click.ignore', function(d) {
52300                 this.blur();    // avoid keeping focus on the button - #4641
52301                 const qaService = services.osmose;
52302                 if (qaService) {
52303                   d.newStatus = 'false';
52304                   qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item));
52305                 }
52306               });
52307           }
52308
52309           // NOTE: Don't change method name until UI v3 is merged
52310           osmoseEditor.error = function(val) {
52311             if (!arguments.length) return _qaItem;
52312             _qaItem = val;
52313             return osmoseEditor;
52314           };
52315
52316           return utilRebind(osmoseEditor, dispatch$1, 'on');
52317         }
52318
52319         // NOTE: Don't change name of this until UI v3 is merged
52320         function modeSelectError(context, selectedErrorID, selectedErrorService) {
52321             var mode = {
52322                 id: 'select-error',
52323                 button: 'browse'
52324             };
52325
52326             var keybinding = utilKeybinding('select-error');
52327
52328             var errorService = services[selectedErrorService];
52329             var errorEditor;
52330             switch (selectedErrorService) {
52331                 case 'improveOSM':
52332                     errorEditor = uiImproveOsmEditor(context)
52333                     .on('change', function() {
52334                         context.map().pan([0,0]);  // trigger a redraw
52335                         var error = checkSelectedID();
52336                         if (!error) return;
52337                         context.ui().sidebar
52338                             .show(errorEditor.error(error));
52339                     });
52340                     break;
52341                 case 'keepRight':
52342                     errorEditor = uiKeepRightEditor(context)
52343                     .on('change', function() {
52344                         context.map().pan([0,0]);  // trigger a redraw
52345                         var error = checkSelectedID();
52346                         if (!error) return;
52347                         context.ui().sidebar
52348                             .show(errorEditor.error(error));
52349                     });
52350                     break;
52351                 case 'osmose':
52352                     errorEditor = uiOsmoseEditor(context)
52353                     .on('change', function() {
52354                         context.map().pan([0,0]);  // trigger a redraw
52355                         var error = checkSelectedID();
52356                         if (!error) return;
52357                         context.ui().sidebar
52358                             .show(errorEditor.error(error));
52359                     });
52360                     break;
52361             }
52362
52363
52364             var behaviors = [
52365                 behaviorBreathe(),
52366                 behaviorHover(context),
52367                 behaviorSelect(context),
52368                 behaviorLasso(context),
52369                 modeDragNode(context).behavior,
52370                 modeDragNote(context).behavior
52371             ];
52372
52373
52374             function checkSelectedID() {
52375                 if (!errorService) return;
52376                 var error = errorService.getError(selectedErrorID);
52377                 if (!error) {
52378                     context.enter(modeBrowse(context));
52379                 }
52380                 return error;
52381             }
52382
52383
52384             mode.zoomToSelected = function() {
52385                 if (!errorService) return;
52386                 var error = errorService.getError(selectedErrorID);
52387                 if (error) {
52388                     context.map().centerZoomEase(error.loc, 20);
52389                 }
52390             };
52391
52392
52393             mode.enter = function() {
52394                 var error = checkSelectedID();
52395                 if (!error) return;
52396
52397                 behaviors.forEach(context.install);
52398                 keybinding
52399                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
52400                     .on('⎋', esc, true);
52401
52402                 select(document)
52403                     .call(keybinding);
52404
52405                 selectError();
52406
52407                 var sidebar = context.ui().sidebar;
52408                 sidebar.show(errorEditor.error(error));
52409
52410                 context.map()
52411                     .on('drawn.select-error', selectError);
52412
52413
52414                 // class the error as selected, or return to browse mode if the error is gone
52415                 function selectError(drawn) {
52416                     if (!checkSelectedID()) return;
52417
52418                     var selection = context.surface()
52419                         .selectAll('.itemId-' + selectedErrorID + '.' + selectedErrorService);
52420
52421                     if (selection.empty()) {
52422                         // Return to browse mode if selected DOM elements have
52423                         // disappeared because the user moved them out of view..
52424                         var source = event && event.type === 'zoom' && event.sourceEvent;
52425                         if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
52426                             context.enter(modeBrowse(context));
52427                         }
52428
52429                     } else {
52430                         selection
52431                             .classed('selected', true);
52432
52433                         context.selectedErrorID(selectedErrorID);
52434                     }
52435                 }
52436
52437                 function esc() {
52438                     if (context.container().select('.combobox').size()) return;
52439                     context.enter(modeBrowse(context));
52440                 }
52441             };
52442
52443
52444             mode.exit = function() {
52445                 behaviors.forEach(context.uninstall);
52446
52447                 select(document)
52448                     .call(keybinding.unbind);
52449
52450                 context.surface()
52451                     .selectAll('.qaItem.selected')
52452                     .classed('selected hover', false);
52453
52454                 context.map()
52455                     .on('drawn.select-error', null);
52456
52457                 context.ui().sidebar
52458                     .hide();
52459
52460                 context.selectedErrorID(null);
52461                 context.features().forceVisible([]);
52462             };
52463
52464
52465             return mode;
52466         }
52467
52468         function behaviorSelect(context) {
52469             var _tolerancePx = 4; // see also behaviorDrag
52470             var _lastMouseEvent = null;
52471             var _showMenu = false;
52472             var _downPointers = {};
52473             var _longPressTimeout = null;
52474             var _lastInteractionType = null;
52475             // the id of the down pointer that's enabling multiselection while down
52476             var _multiselectionPointerId = null;
52477
52478             // use pointer events on supported platforms; fallback to mouse events
52479             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
52480
52481
52482             function keydown() {
52483
52484                 if (event.keyCode === 32) {
52485                     // don't react to spacebar events during text input
52486                     var activeNode = document.activeElement;
52487                     if (activeNode && new Set(['INPUT', 'TEXTAREA']).has(activeNode.nodeName)) return;
52488                 }
52489
52490                 if (event.keyCode === 93 ||  // context menu key
52491                     event.keyCode === 32) {  // spacebar
52492                     event.preventDefault();
52493                 }
52494
52495                 if (event.repeat) return; // ignore repeated events for held keys
52496
52497                 // if any key is pressed the user is probably doing something other than long-pressing
52498                 cancelLongPress();
52499
52500                 if (event.shiftKey) {
52501                     context.surface()
52502                         .classed('behavior-multiselect', true);
52503                 }
52504
52505                 if (event.keyCode === 32) {  // spacebar
52506                     if (!_downPointers.spacebar && _lastMouseEvent) {
52507                         cancelLongPress();
52508                         _longPressTimeout = window.setTimeout(didLongPress, 500, 'spacebar', 'spacebar');
52509
52510                         _downPointers.spacebar = {
52511                             firstEvent: _lastMouseEvent,
52512                             lastEvent: _lastMouseEvent
52513                         };
52514                     }
52515                 }
52516             }
52517
52518
52519             function keyup() {
52520                 cancelLongPress();
52521
52522                 if (!event.shiftKey) {
52523                     context.surface()
52524                         .classed('behavior-multiselect', false);
52525                 }
52526
52527                 if (event.keyCode === 93) {  // context menu key
52528                     event.preventDefault();
52529                     _lastInteractionType = 'menukey';
52530                     contextmenu();
52531                 } else if (event.keyCode === 32) {  // spacebar
52532                     var pointer = _downPointers.spacebar;
52533                     if (pointer) {
52534                         delete _downPointers.spacebar;
52535
52536                         if (pointer.done) return;
52537
52538                         event.preventDefault();
52539                         _lastInteractionType = 'spacebar';
52540                         click(pointer.firstEvent, pointer.lastEvent, 'spacebar');
52541                     }
52542                 }
52543             }
52544
52545
52546             function pointerdown() {
52547                 var id = (event.pointerId || 'mouse').toString();
52548
52549                 cancelLongPress();
52550
52551                 if (event.buttons && event.buttons !== 1) return;
52552
52553                 context.ui().closeEditMenu();
52554
52555                 _longPressTimeout = window.setTimeout(didLongPress, 500, id, 'longdown-' + (event.pointerType || 'mouse'));
52556
52557                 _downPointers[id] = {
52558                     firstEvent: event,
52559                     lastEvent: event
52560                 };
52561             }
52562
52563
52564             function didLongPress(id, interactionType) {
52565                 var pointer = _downPointers[id];
52566                 if (!pointer) return;
52567
52568                 for (var i in _downPointers) {
52569                     // don't allow this or any currently down pointer to trigger another click
52570                     _downPointers[i].done = true;
52571                 }
52572
52573                 // treat long presses like right-clicks
52574                 _longPressTimeout = null;
52575                 _lastInteractionType = interactionType;
52576                 _showMenu = true;
52577
52578                 click(pointer.firstEvent, pointer.lastEvent, id);
52579             }
52580
52581
52582             function pointermove() {
52583                 var id = (event.pointerId || 'mouse').toString();
52584                 if (_downPointers[id]) {
52585                     _downPointers[id].lastEvent = event;
52586                 }
52587                 if (!event.pointerType || event.pointerType === 'mouse') {
52588                     _lastMouseEvent = event;
52589                     if (_downPointers.spacebar) {
52590                         _downPointers.spacebar.lastEvent = event;
52591                     }
52592                 }
52593             }
52594
52595
52596             function pointerup() {
52597                 var id = (event.pointerId || 'mouse').toString();
52598                 var pointer = _downPointers[id];
52599                 if (!pointer) return;
52600
52601                 delete _downPointers[id];
52602
52603                 if (_multiselectionPointerId === id) {
52604                     _multiselectionPointerId = null;
52605                 }
52606
52607                 if (pointer.done) return;
52608
52609                 click(pointer.firstEvent, event, id);
52610             }
52611
52612
52613             function pointercancel() {
52614                 var id = (event.pointerId || 'mouse').toString();
52615                 if (!_downPointers[id]) return;
52616
52617                 delete _downPointers[id];
52618
52619                 if (_multiselectionPointerId === id) {
52620                     _multiselectionPointerId = null;
52621                 }
52622             }
52623
52624
52625             function contextmenu() {
52626                 var e = event;
52627                 e.preventDefault();
52628
52629                 if (!+e.clientX && !+e.clientY) {
52630                     if (_lastMouseEvent) {
52631                         e.sourceEvent = _lastMouseEvent;
52632                     } else {
52633                         return;
52634                     }
52635                 } else {
52636                     _lastMouseEvent = event;
52637                     _lastInteractionType = 'rightclick';
52638                 }
52639
52640                 _showMenu = true;
52641                 click(event, event);
52642             }
52643
52644
52645             function click(firstEvent, lastEvent, pointerId) {
52646                 cancelLongPress();
52647
52648                 var mapNode = context.container().select('.main-map').node();
52649
52650                 // Use the `main-map` coordinate system since the surface and supersurface
52651                 // are transformed when drag-panning.
52652                 var pointGetter = utilFastMouse(mapNode);
52653                 var p1 = pointGetter(firstEvent);
52654                 var p2 = pointGetter(lastEvent);
52655                 var dist = geoVecLength(p1, p2);
52656
52657                 if (dist > _tolerancePx ||
52658                     !mapContains(lastEvent)) {
52659
52660                     resetProperties();
52661                     return;
52662                 }
52663
52664                 var targetDatum = lastEvent.target.__data__;
52665
52666                 var multiselectEntityId;
52667
52668                 if (!_multiselectionPointerId) {
52669                     // If a different pointer than the one triggering this click is down on a
52670                     // feature, treat this and all future clicks as multiselection until that
52671                     // pointer is raised.
52672                     var selectPointerInfo = pointerDownOnSelection(pointerId);
52673                     if (selectPointerInfo) {
52674                         _multiselectionPointerId = selectPointerInfo.pointerId;
52675                         // if the other feature isn't selected yet, make sure we select it
52676                         multiselectEntityId = !selectPointerInfo.selected && selectPointerInfo.entityId;
52677                         _downPointers[selectPointerInfo.pointerId].done = true;
52678                     }
52679                 }
52680
52681                 // support multiselect if data is already selected
52682                 var isMultiselect = context.mode().id === 'select' && (
52683                     // and shift key is down
52684                     (event && event.shiftKey) ||
52685                     // or we're lasso-selecting
52686                     context.surface().select('.lasso').node() ||
52687                     // or a pointer is down over a selected feature
52688                     (_multiselectionPointerId && !multiselectEntityId)
52689                 );
52690
52691                 processClick(targetDatum, isMultiselect, p2, multiselectEntityId);
52692
52693                 function mapContains(event) {
52694                     var rect = mapNode.getBoundingClientRect();
52695                     return event.clientX >= rect.left &&
52696                         event.clientX <= rect.right &&
52697                         event.clientY >= rect.top &&
52698                         event.clientY <= rect.bottom;
52699                 }
52700
52701                 function pointerDownOnSelection(skipPointerId) {
52702                     var mode = context.mode();
52703                     var selectedIDs = mode.id === 'select' ? mode.selectedIDs() : [];
52704                     for (var pointerId in _downPointers) {
52705                         if (pointerId === 'spacebar' || pointerId === skipPointerId) continue;
52706
52707                         var pointerInfo = _downPointers[pointerId];
52708
52709                         var p1 = pointGetter(pointerInfo.firstEvent);
52710                         var p2 = pointGetter(pointerInfo.lastEvent);
52711                         if (geoVecLength(p1, p2) > _tolerancePx) continue;
52712
52713                         var datum = pointerInfo.firstEvent.target.__data__;
52714                         var entity = (datum && datum.properties && datum.properties.entity) || datum;
52715                         if (context.graph().hasEntity(entity.id)) return {
52716                             pointerId: pointerId,
52717                             entityId: entity.id,
52718                             selected: selectedIDs.indexOf(entity.id) !== -1
52719                         };
52720                     }
52721                     return null;
52722                 }
52723             }
52724
52725
52726             function processClick(datum, isMultiselect, point, alsoSelectId) {
52727                 var mode = context.mode();
52728                 var showMenu = _showMenu;
52729                 var interactionType = _lastInteractionType;
52730
52731                 var entity = datum && datum.properties && datum.properties.entity;
52732                 if (entity) datum = entity;
52733
52734                 if (datum && datum.type === 'midpoint') {
52735                     // treat targeting midpoints as if targeting the parent way
52736                     datum = datum.parents[0];
52737                 }
52738
52739                 var newMode;
52740
52741                 if (datum instanceof osmEntity) {
52742                     // targeting an entity
52743                     var selectedIDs = context.selectedIDs();
52744                     context.selectedNoteID(null);
52745                     context.selectedErrorID(null);
52746
52747                     if (!isMultiselect) {
52748                         // don't change the selection if we're toggling the menu atop a multiselection
52749                         if (!showMenu ||
52750                             selectedIDs.length <= 1 ||
52751                             selectedIDs.indexOf(datum.id) === -1) {
52752
52753                             if (alsoSelectId === datum.id) alsoSelectId = null;
52754
52755                             selectedIDs = (alsoSelectId ? [alsoSelectId] : []).concat([datum.id]);
52756                             // always enter modeSelect even if the entity is already
52757                             // selected since listeners may expect `context.enter` events,
52758                             // e.g. in the walkthrough
52759                             newMode = mode.id === 'select' ? mode.selectedIDs(selectedIDs) : modeSelect(context, selectedIDs).selectBehavior(behavior);
52760                             context.enter(newMode);
52761                         }
52762
52763                     } else {
52764                         if (selectedIDs.indexOf(datum.id) !== -1) {
52765                             // clicked entity is already in the selectedIDs list..
52766                             if (!showMenu) {
52767                                 // deselect clicked entity, then reenter select mode or return to browse mode..
52768                                 selectedIDs = selectedIDs.filter(function(id) { return id !== datum.id; });
52769                                 newMode = selectedIDs.length ? mode.selectedIDs(selectedIDs) : modeBrowse(context).selectBehavior(behavior);
52770                                 context.enter(newMode);
52771                             }
52772                         } else {
52773                             // clicked entity is not in the selected list, add it..
52774                             selectedIDs = selectedIDs.concat([datum.id]);
52775                             newMode = mode.selectedIDs(selectedIDs);
52776                             context.enter(newMode);
52777                         }
52778                     }
52779
52780                 } else if (datum && datum.__featurehash__ && !isMultiselect) {
52781                     // targeting custom data
52782                     context
52783                         .selectedNoteID(null)
52784                         .enter(modeSelectData(context, datum));
52785
52786                 } else if (datum instanceof osmNote && !isMultiselect) {
52787                     // targeting a note
52788                     context
52789                         .selectedNoteID(datum.id)
52790                         .enter(modeSelectNote(context, datum.id));
52791
52792                 } else if (datum instanceof QAItem & !isMultiselect) {
52793                     // targeting an external QA issue
52794                     context
52795                         .selectedErrorID(datum.id)
52796                         .enter(modeSelectError(context, datum.id, datum.service));
52797
52798                 } else {
52799                     // targeting nothing
52800                     context.selectedNoteID(null);
52801                     context.selectedErrorID(null);
52802                     if (!isMultiselect && mode.id !== 'browse') {
52803                         context.enter(modeBrowse(context));
52804                     }
52805                 }
52806
52807                 context.ui().closeEditMenu();
52808
52809                 // always request to show the edit menu in case the mode needs it
52810                 if (showMenu) context.ui().showEditMenu(point, interactionType);
52811
52812                 resetProperties();
52813             }
52814
52815
52816             function cancelLongPress() {
52817                 if (_longPressTimeout) window.clearTimeout(_longPressTimeout);
52818                 _longPressTimeout = null;
52819             }
52820
52821
52822             function resetProperties() {
52823                 cancelLongPress();
52824                 _showMenu = false;
52825                 _lastInteractionType = null;
52826                 // don't reset _lastMouseEvent since it might still be useful
52827             }
52828
52829
52830             function behavior(selection) {
52831                 resetProperties();
52832                 _lastMouseEvent = context.map().lastPointerEvent();
52833
52834                 select(window)
52835                     .on('keydown.select', keydown)
52836                     .on('keyup.select', keyup)
52837                     .on(_pointerPrefix + 'move.select', pointermove, true)
52838                     .on(_pointerPrefix + 'up.select', pointerup, true)
52839                     .on('pointercancel.select', pointercancel, true)
52840                     .on('contextmenu.select-window', function() {
52841                         // Edge and IE really like to show the contextmenu on the
52842                         // menubar when user presses a keyboard menu button
52843                         // even after we've already preventdefaulted the key event.
52844                         var e = event;
52845                         if (+e.clientX === 0 && +e.clientY === 0) {
52846                             event.preventDefault();
52847                         }
52848                     });
52849
52850                 selection
52851                     .on(_pointerPrefix + 'down.select', pointerdown)
52852                     .on('contextmenu.select', contextmenu);
52853
52854                 if (event && event.shiftKey) {
52855                     context.surface()
52856                         .classed('behavior-multiselect', true);
52857                 }
52858             }
52859
52860
52861             behavior.off = function(selection) {
52862                 cancelLongPress();
52863
52864                 select(window)
52865                     .on('keydown.select', null)
52866                     .on('keyup.select', null)
52867                     .on('contextmenu.select-window', null)
52868                     .on(_pointerPrefix + 'move.select', null, true)
52869                     .on(_pointerPrefix + 'up.select', null, true)
52870                     .on('pointercancel.select', null, true);
52871
52872                 selection
52873                     .on(_pointerPrefix + 'down.select', null)
52874                     .on('contextmenu.select', null);
52875
52876                 context.surface()
52877                     .classed('behavior-multiselect', false);
52878             };
52879
52880
52881             return behavior;
52882         }
52883
52884         function behaviorDrawWay(context, wayID, mode, startGraph) {
52885
52886             var dispatch$1 = dispatch('rejectedSelfIntersection');
52887
52888             var behavior = behaviorDraw(context);
52889
52890             // Must be set by `drawWay.nodeIndex` before each install of this behavior.
52891             var _nodeIndex;
52892
52893             var _origWay;
52894             var _wayGeometry;
52895             var _headNodeID;
52896             var _annotation;
52897
52898             var _pointerHasMoved = false;
52899
52900             // The osmNode to be placed.
52901             // This is temporary and just follows the mouse cursor until an "add" event occurs.
52902             var _drawNode;
52903
52904             var _didResolveTempEdit = false;
52905
52906             function createDrawNode(loc) {
52907                 // don't make the draw node until we actually need it
52908                 _drawNode = osmNode({ loc: loc });
52909
52910                 context.pauseChangeDispatch();
52911                 context.replace(function actionAddDrawNode(graph) {
52912                     // add the draw node to the graph and insert it into the way
52913                     var way = graph.entity(wayID);
52914                     return graph
52915                         .replace(_drawNode)
52916                         .replace(way.addNode(_drawNode.id, _nodeIndex));
52917                 }, _annotation);
52918                 context.resumeChangeDispatch();
52919
52920                 setActiveElements();
52921             }
52922
52923             function removeDrawNode() {
52924
52925                 context.pauseChangeDispatch();
52926                 context.replace(
52927                     function actionDeleteDrawNode(graph) {
52928                        var way = graph.entity(wayID);
52929                        return graph
52930                            .replace(way.removeNode(_drawNode.id))
52931                            .remove(_drawNode);
52932                    },
52933                     _annotation
52934                 );
52935                 _drawNode = undefined;
52936                 context.resumeChangeDispatch();
52937             }
52938
52939
52940             function keydown() {
52941                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
52942                     if (context.surface().classed('nope')) {
52943                         context.surface()
52944                             .classed('nope-suppressed', true);
52945                     }
52946                     context.surface()
52947                         .classed('nope', false)
52948                         .classed('nope-disabled', true);
52949                 }
52950             }
52951
52952
52953             function keyup() {
52954                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
52955                     if (context.surface().classed('nope-suppressed')) {
52956                         context.surface()
52957                             .classed('nope', true);
52958                     }
52959                     context.surface()
52960                         .classed('nope-suppressed', false)
52961                         .classed('nope-disabled', false);
52962                 }
52963             }
52964
52965
52966             function allowsVertex(d) {
52967                 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
52968             }
52969
52970
52971             // related code
52972             // - `mode/drag_node.js`     `doMove()`
52973             // - `behavior/draw.js`      `click()`
52974             // - `behavior/draw_way.js`  `move()`
52975             function move(datum) {
52976
52977                 var loc = context.map().mouseCoordinates();
52978
52979                 if (!_drawNode) createDrawNode(loc);
52980
52981                 context.surface().classed('nope-disabled', event.altKey);
52982
52983                 var targetLoc = datum && datum.properties && datum.properties.entity &&
52984                     allowsVertex(datum.properties.entity) && datum.properties.entity.loc;
52985                 var targetNodes = datum && datum.properties && datum.properties.nodes;
52986
52987                 if (targetLoc) {   // snap to node/vertex - a point target with `.loc`
52988                     loc = targetLoc;
52989
52990                 } else if (targetNodes) {   // snap to way - a line target with `.nodes`
52991                     var choice = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, _drawNode.id);
52992                     if (choice) {
52993                         loc = choice.loc;
52994                     }
52995                 }
52996
52997                 context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
52998                 _drawNode = context.entity(_drawNode.id);
52999                 checkGeometry(true /* includeDrawNode */);
53000             }
53001
53002
53003             // Check whether this edit causes the geometry to break.
53004             // If so, class the surface with a nope cursor.
53005             // `includeDrawNode` - Only check the relevant line segments if finishing drawing
53006             function checkGeometry(includeDrawNode) {
53007                 var nopeDisabled = context.surface().classed('nope-disabled');
53008                 var isInvalid = isInvalidGeometry(includeDrawNode);
53009
53010                 if (nopeDisabled) {
53011                     context.surface()
53012                         .classed('nope', false)
53013                         .classed('nope-suppressed', isInvalid);
53014                 } else {
53015                     context.surface()
53016                         .classed('nope', isInvalid)
53017                         .classed('nope-suppressed', false);
53018                 }
53019             }
53020
53021
53022             function isInvalidGeometry(includeDrawNode) {
53023
53024                 var testNode = _drawNode;
53025
53026                 // we only need to test the single way we're drawing
53027                 var parentWay = context.graph().entity(wayID);
53028                 var nodes = context.graph().childNodes(parentWay).slice();  // shallow copy
53029
53030                 if (includeDrawNode) {
53031                     if (parentWay.isClosed()) {
53032                         // don't test the last segment for closed ways - #4655
53033                         // (still test the first segement)
53034                         nodes.pop();
53035                     }
53036                 } else { // discount the draw node
53037
53038                     if (parentWay.isClosed()) {
53039                         if (nodes.length < 3) return false;
53040                         if (_drawNode) nodes.splice(-2, 1);
53041                         testNode = nodes[nodes.length - 2];
53042                     } else {
53043                         // there's nothing we need to test if we ignore the draw node on open ways
53044                         return false;
53045                     }
53046                 }
53047
53048                 return testNode && geoHasSelfIntersections(nodes, testNode.id);
53049             }
53050
53051
53052             function undone() {
53053
53054                 // undoing removed the temp edit
53055                 _didResolveTempEdit = true;
53056
53057                 context.pauseChangeDispatch();
53058
53059                 var nextMode;
53060
53061                 if (context.graph() === startGraph) { // we've undone back to the beginning
53062                     nextMode = modeSelect(context, [wayID]);
53063                 } else {
53064                     context.history()
53065                         .on('undone.draw', null);
53066                     // remove whatever segment was drawn previously
53067                     context.undo();
53068
53069                     if (context.graph() === startGraph) { // we've undone back to the beginning
53070                         nextMode = modeSelect(context, [wayID]);
53071                     } else {
53072                         // continue drawing
53073                         nextMode = mode;
53074                     }
53075                 }
53076
53077                 // clear the redo stack by adding and removing an edit
53078                 context.perform(actionNoop());
53079                 context.pop(1);
53080
53081                 context.resumeChangeDispatch();
53082                 context.enter(nextMode);
53083             }
53084
53085
53086             function setActiveElements() {
53087                 if (!_drawNode) return;
53088
53089                 context.surface().selectAll('.' + _drawNode.id)
53090                     .classed('active', true);
53091             }
53092
53093
53094             function resetToStartGraph() {
53095                 while (context.graph() !== startGraph) {
53096                     context.pop();
53097                 }
53098             }
53099
53100
53101             var drawWay = function(surface) {
53102                 _drawNode = undefined;
53103                 _didResolveTempEdit = false;
53104                 _origWay = context.entity(wayID);
53105                 _headNodeID = typeof _nodeIndex === 'number' ? _origWay.nodes[_nodeIndex] :
53106                     (_origWay.isClosed() ? _origWay.nodes[_origWay.nodes.length - 2] : _origWay.nodes[_origWay.nodes.length - 1]);
53107                 _wayGeometry = _origWay.geometry(context.graph());
53108                 _annotation = _t((_origWay.isDegenerate() ?
53109                     'operations.start.annotation.' :
53110                     'operations.continue.annotation.') + _wayGeometry
53111                 );
53112                 _pointerHasMoved = false;
53113
53114                 // Push an annotated state for undo to return back to.
53115                 // We must make sure to replace or remove it later.
53116                 context.pauseChangeDispatch();
53117                 context.perform(actionNoop(), _annotation);
53118                 context.resumeChangeDispatch();
53119
53120                 behavior.hover()
53121                     .initialNodeID(_headNodeID);
53122
53123                 behavior
53124                     .on('move', function() {
53125                         _pointerHasMoved = true;
53126                         move.apply(this, arguments);
53127                     })
53128                     .on('down', function() {
53129                         move.apply(this, arguments);
53130                     })
53131                     .on('downcancel', function() {
53132                         if (_drawNode) removeDrawNode();
53133                     })
53134                     .on('click', drawWay.add)
53135                     .on('clickWay', drawWay.addWay)
53136                     .on('clickNode', drawWay.addNode)
53137                     .on('undo', context.undo)
53138                     .on('cancel', drawWay.cancel)
53139                     .on('finish', drawWay.finish);
53140
53141                 select(window)
53142                     .on('keydown.drawWay', keydown)
53143                     .on('keyup.drawWay', keyup);
53144
53145                 context.map()
53146                     .dblclickZoomEnable(false)
53147                     .on('drawn.draw', setActiveElements);
53148
53149                 setActiveElements();
53150
53151                 surface.call(behavior);
53152
53153                 context.history()
53154                     .on('undone.draw', undone);
53155             };
53156
53157
53158             drawWay.off = function(surface) {
53159
53160                 if (!_didResolveTempEdit) {
53161                     // Drawing was interrupted unexpectedly.
53162                     // This can happen if the user changes modes,
53163                     // clicks geolocate button, a hashchange event occurs, etc.
53164
53165                     context.pauseChangeDispatch();
53166                     resetToStartGraph();
53167                     context.resumeChangeDispatch();
53168                 }
53169
53170                 _drawNode = undefined;
53171                 _nodeIndex = undefined;
53172
53173                 context.map()
53174                     .on('drawn.draw', null);
53175
53176                 surface.call(behavior.off)
53177                     .selectAll('.active')
53178                     .classed('active', false);
53179
53180                 surface
53181                     .classed('nope', false)
53182                     .classed('nope-suppressed', false)
53183                     .classed('nope-disabled', false);
53184
53185                 select(window)
53186                     .on('keydown.drawWay', null)
53187                     .on('keyup.drawWay', null);
53188
53189                 context.history()
53190                     .on('undone.draw', null);
53191             };
53192
53193
53194             function attemptAdd(d, loc, doAdd) {
53195
53196                 if (_drawNode) {
53197                     // move the node to the final loc in case move wasn't called
53198                     // consistently (e.g. on touch devices)
53199                     context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
53200                     _drawNode = context.entity(_drawNode.id);
53201                 } else {
53202                     createDrawNode(loc);
53203                 }
53204
53205                 checkGeometry(true /* includeDrawNode */);
53206                 if ((d && d.properties && d.properties.nope) || context.surface().classed('nope')) {
53207                     if (!_pointerHasMoved) {
53208                         // prevent the temporary draw node from appearing on touch devices
53209                         removeDrawNode();
53210                     }
53211                     dispatch$1.call('rejectedSelfIntersection', this);
53212                     return;   // can't click here
53213                 }
53214
53215                 context.pauseChangeDispatch();
53216                 doAdd();
53217                 // we just replaced the temporary edit with the real one
53218                 _didResolveTempEdit = true;
53219                 context.resumeChangeDispatch();
53220
53221                 context.enter(mode);
53222             }
53223
53224
53225             // Accept the current position of the drawing node
53226             drawWay.add = function(loc, d) {
53227                 attemptAdd(d, loc, function() {
53228                     // don't need to do anything extra
53229                 });
53230             };
53231
53232
53233             // Connect the way to an existing way
53234             drawWay.addWay = function(loc, edge, d) {
53235                 attemptAdd(d, loc, function() {
53236                     context.replace(
53237                         actionAddMidpoint({ loc: loc, edge: edge }, _drawNode),
53238                         _annotation
53239                     );
53240                 });
53241             };
53242
53243
53244             // Connect the way to an existing node
53245             drawWay.addNode = function(node, d) {
53246
53247                 // finish drawing if the mapper targets the prior node
53248                 if (node.id === _headNodeID ||
53249                     // or the first node when drawing an area
53250                     (_origWay.isClosed() && node.id === _origWay.first())) {
53251                     drawWay.finish();
53252                     return;
53253                 }
53254
53255                 attemptAdd(d, node.loc, function() {
53256                     context.replace(
53257                         function actionReplaceDrawNode(graph) {
53258                             // remove the temporary draw node and insert the existing node
53259                             // at the same index
53260
53261                             graph = graph
53262                                 .replace(graph.entity(wayID).removeNode(_drawNode.id))
53263                                 .remove(_drawNode);
53264                             return graph
53265                                 .replace(graph.entity(wayID).addNode(node.id, _nodeIndex));
53266                         },
53267                         _annotation
53268                     );
53269                 });
53270             };
53271
53272
53273             // Finish the draw operation, removing the temporary edit.
53274             // If the way has enough nodes to be valid, it's selected.
53275             // Otherwise, delete everything and return to browse mode.
53276             drawWay.finish = function() {
53277                 checkGeometry(false /* includeDrawNode */);
53278                 if (context.surface().classed('nope')) {
53279                     dispatch$1.call('rejectedSelfIntersection', this);
53280                     return;   // can't click here
53281                 }
53282
53283                 context.pauseChangeDispatch();
53284                 // remove the temporary edit
53285                 context.pop(1);
53286                 _didResolveTempEdit = true;
53287                 context.resumeChangeDispatch();
53288
53289                 var way = context.hasEntity(wayID);
53290                 if (!way || way.isDegenerate()) {
53291                     drawWay.cancel();
53292                     return;
53293                 }
53294
53295                 window.setTimeout(function() {
53296                     context.map().dblclickZoomEnable(true);
53297                 }, 1000);
53298
53299                 var isNewFeature = !mode.isContinuing;
53300                 context.enter(modeSelect(context, [wayID]).newFeature(isNewFeature));
53301             };
53302
53303
53304             // Cancel the draw operation, delete everything, and return to browse mode.
53305             drawWay.cancel = function() {
53306                 context.pauseChangeDispatch();
53307                 resetToStartGraph();
53308                 context.resumeChangeDispatch();
53309
53310                 window.setTimeout(function() {
53311                     context.map().dblclickZoomEnable(true);
53312                 }, 1000);
53313
53314                 context.surface()
53315                     .classed('nope', false)
53316                     .classed('nope-disabled', false)
53317                     .classed('nope-suppressed', false);
53318
53319                 context.enter(modeBrowse(context));
53320             };
53321
53322
53323             drawWay.nodeIndex = function(val) {
53324                 if (!arguments.length) return _nodeIndex;
53325                 _nodeIndex = val;
53326                 return drawWay;
53327             };
53328
53329
53330             drawWay.activeID = function() {
53331                 if (!arguments.length) return _drawNode && _drawNode.id;
53332                 // no assign
53333                 return drawWay;
53334             };
53335
53336
53337             return utilRebind(drawWay, dispatch$1, 'on');
53338         }
53339
53340         function modeDrawLine(context, wayID, startGraph, button, affix, continuing) {
53341             var mode = {
53342                 button: button,
53343                 id: 'draw-line'
53344             };
53345
53346             var behavior = behaviorDrawWay(context, wayID, mode, startGraph)
53347                 .on('rejectedSelfIntersection.modeDrawLine', function() {
53348                     context.ui().flash
53349                         .text(_t('self_intersection.error.lines'))();
53350                 });
53351
53352             mode.wayID = wayID;
53353
53354             mode.isContinuing = continuing;
53355
53356             mode.enter = function() {
53357                 behavior
53358                     .nodeIndex(affix === 'prefix' ? 0 : undefined);
53359
53360                 context.install(behavior);
53361             };
53362
53363             mode.exit = function() {
53364                 context.uninstall(behavior);
53365             };
53366
53367             mode.selectedIDs = function() {
53368                 return [wayID];
53369             };
53370
53371             mode.activeID = function() {
53372                 return (behavior && behavior.activeID()) || [];
53373             };
53374
53375             return mode;
53376         }
53377
53378         function operationContinue(context, selectedIDs) {
53379             var graph = context.graph();
53380             var entities = selectedIDs.map(function(id) { return graph.entity(id); });
53381             var geometries = Object.assign(
53382                 { line: [], vertex: [] },
53383                 utilArrayGroupBy(entities, function(entity) { return entity.geometry(graph); })
53384             );
53385             var vertex = geometries.vertex[0];
53386
53387
53388             function candidateWays() {
53389                 return graph.parentWays(vertex).filter(function(parent) {
53390                     return parent.geometry(graph) === 'line' &&
53391                         !parent.isClosed() &&
53392                         parent.affix(vertex.id) &&
53393                         (geometries.line.length === 0 || geometries.line[0] === parent);
53394                 });
53395             }
53396
53397
53398             var operation = function() {
53399                 var candidate = candidateWays()[0];
53400                 context.enter(
53401                     modeDrawLine(context, candidate.id, context.graph(), 'line', candidate.affix(vertex.id), true)
53402                 );
53403             };
53404
53405
53406             operation.available = function() {
53407                 return geometries.vertex.length === 1 &&
53408                     geometries.line.length <= 1 &&
53409                     !context.features().hasHiddenConnections(vertex, context.graph());
53410             };
53411
53412
53413             operation.disabled = function() {
53414                 var candidates = candidateWays();
53415                 if (candidates.length === 0) {
53416                     return 'not_eligible';
53417                 } else if (candidates.length > 1) {
53418                     return 'multiple';
53419                 }
53420
53421                 return false;
53422             };
53423
53424
53425             operation.tooltip = function() {
53426                 var disable = operation.disabled();
53427                 return disable ?
53428                     _t('operations.continue.' + disable) :
53429                     _t('operations.continue.description');
53430             };
53431
53432
53433             operation.annotation = function() {
53434                 return _t('operations.continue.annotation.line');
53435             };
53436
53437
53438             operation.id = 'continue';
53439             operation.keys = [_t('operations.continue.key')];
53440             operation.title = _t('operations.continue.title');
53441             operation.behavior = behaviorOperation(context).which(operation);
53442
53443             return operation;
53444         }
53445
53446         function operationCopy(context, selectedIDs) {
53447
53448             var _multi = selectedIDs.length === 1 ? 'single' : 'multiple';
53449
53450             function getFilteredIdsToCopy() {
53451                 return selectedIDs.filter(function(selectedID) {
53452                     var entity = context.graph().hasEntity(selectedID);
53453                     // don't copy untagged vertices separately from ways
53454                     return entity.hasInterestingTags() || entity.geometry(context.graph()) !== 'vertex';
53455                 });
53456             }
53457
53458             var operation = function() {
53459
53460                 if (!getSelectionText()) {
53461                     event.preventDefault();
53462                 }
53463
53464                 var graph = context.graph();
53465                 var selected = groupEntities(getFilteredIdsToCopy(), graph);
53466                 var canCopy = [];
53467                 var skip = {};
53468                 var entity;
53469                 var i;
53470
53471                 for (i = 0; i < selected.relation.length; i++) {
53472                     entity = selected.relation[i];
53473                     if (!skip[entity.id] && entity.isComplete(graph)) {
53474                         canCopy.push(entity.id);
53475                         skip = getDescendants(entity.id, graph, skip);
53476                     }
53477                 }
53478                 for (i = 0; i < selected.way.length; i++) {
53479                     entity = selected.way[i];
53480                     if (!skip[entity.id]) {
53481                         canCopy.push(entity.id);
53482                         skip = getDescendants(entity.id, graph, skip);
53483                     }
53484                 }
53485                 for (i = 0; i < selected.node.length; i++) {
53486                     entity = selected.node[i];
53487                     if (!skip[entity.id]) {
53488                         canCopy.push(entity.id);
53489                     }
53490                 }
53491
53492                 context.copyIDs(canCopy);
53493                 if (_point &&
53494                     (canCopy.length !== 1 || graph.entity(canCopy[0]).type !== 'node')) {
53495                     // store the anchor coordinates if copying more than a single node
53496                     context.copyLonLat(context.projection.invert(_point));
53497                 } else {
53498                     context.copyLonLat(null);
53499                 }
53500
53501             };
53502
53503
53504             function groupEntities(ids, graph) {
53505                 var entities = ids.map(function (id) { return graph.entity(id); });
53506                 return Object.assign(
53507                     { relation: [], way: [], node: [] },
53508                     utilArrayGroupBy(entities, 'type')
53509                 );
53510             }
53511
53512
53513             function getDescendants(id, graph, descendants) {
53514                 var entity = graph.entity(id);
53515                 var children;
53516
53517                 descendants = descendants || {};
53518
53519                 if (entity.type === 'relation') {
53520                     children = entity.members.map(function(m) { return m.id; });
53521                 } else if (entity.type === 'way') {
53522                     children = entity.nodes;
53523                 } else {
53524                     children = [];
53525                 }
53526
53527                 for (var i = 0; i < children.length; i++) {
53528                     if (!descendants[children[i]]) {
53529                         descendants[children[i]] = true;
53530                         descendants = getDescendants(children[i], graph, descendants);
53531                     }
53532                 }
53533
53534                 return descendants;
53535             }
53536
53537
53538             function getSelectionText() {
53539                 return window.getSelection().toString();
53540             }
53541
53542
53543             operation.available = function() {
53544                 return getFilteredIdsToCopy().length > 0;
53545             };
53546
53547
53548             operation.disabled = function() {
53549                 var extent = utilTotalExtent(getFilteredIdsToCopy(), context.graph());
53550                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
53551                     return 'too_large';
53552                 }
53553                 return false;
53554             };
53555
53556
53557             operation.tooltip = function() {
53558                 var disable = operation.disabled();
53559                 return disable ?
53560                     _t('operations.copy.' + disable + '.' + _multi) :
53561                     _t('operations.copy.description' + '.' + _multi);
53562             };
53563
53564
53565             operation.annotation = function() {
53566                 return selectedIDs.length === 1 ?
53567                     _t('operations.copy.annotation.single') :
53568                     _t('operations.copy.annotation.multiple', { n: selectedIDs.length.toString() });
53569             };
53570
53571
53572             var _point;
53573             operation.point = function(val) {
53574                 _point = val;
53575                 return operation;
53576             };
53577
53578
53579             operation.id = 'copy';
53580             operation.keys = [uiCmd('⌘C')];
53581             operation.title = _t('operations.copy.title');
53582             operation.behavior = behaviorOperation(context).which(operation);
53583
53584             return operation;
53585         }
53586
53587         function operationDisconnect(context, selectedIDs) {
53588             var _vertexIDs = [];
53589             var _wayIDs = [];
53590             var _otherIDs = [];
53591             var _actions = [];
53592
53593             selectedIDs.forEach(function(id) {
53594                 var entity = context.entity(id);
53595                 if (entity.type === 'way'){
53596                     _wayIDs.push(id);
53597                 } else if (entity.geometry(context.graph()) === 'vertex') {
53598                     _vertexIDs.push(id);
53599                 } else {
53600                     _otherIDs.push(id);
53601                 }
53602             });
53603
53604             var _extent, _nodes, _coords, _descriptionID = '', _annotationID = 'features';
53605
53606             if (_vertexIDs.length > 0) {
53607                 // At the selected vertices, disconnect the selected ways, if any, else
53608                 // disconnect all connected ways
53609
53610                 _extent = utilTotalExtent(_vertexIDs, context.graph());
53611
53612                 _vertexIDs.forEach(function(vertexID) {
53613                     var action = actionDisconnect(vertexID);
53614
53615                     if (_wayIDs.length > 0) {
53616                         var waysIDsForVertex = _wayIDs.filter(function(wayID) {
53617                             var way = context.entity(wayID);
53618                             return way.nodes.indexOf(vertexID) !== -1;
53619                         });
53620                         action.limitWays(waysIDsForVertex);
53621                     }
53622                     _actions.push(action);
53623                 });
53624
53625                 _descriptionID += _actions.length === 1 ? 'single_point.' : 'multiple_points.';
53626                 if (_wayIDs.length === 1) {
53627                     _descriptionID += 'single_way.' + context.graph().geometry(_wayIDs[0]);
53628                 } else {
53629                     _descriptionID += _wayIDs.length === 0 ? 'no_ways' : 'multiple_ways';
53630                 }
53631
53632             } else if (_wayIDs.length > 0) {
53633                 // Disconnect the selected ways from each other, if they're connected,
53634                 // else disconnect them from all connected ways
53635
53636                 var ways = _wayIDs.map(function(id) {
53637                     return context.entity(id);
53638                 });
53639                 _nodes = utilGetAllNodes(_wayIDs, context.graph());
53640                 _coords = _nodes.map(function(n) { return n.loc; });
53641                 _extent = utilTotalExtent(ways, context.graph());
53642
53643                 // actions for connected nodes shared by at least two selected ways
53644                 var sharedActions = [];
53645                 // actions for connected nodes
53646                 var unsharedActions = [];
53647
53648                 _nodes.forEach(function(node) {
53649                     var action = actionDisconnect(node.id).limitWays(_wayIDs);
53650                     if (action.disabled(context.graph()) !== 'not_connected') {
53651
53652                         var count = 0;
53653                         for (var i in ways) {
53654                             var way = ways[i];
53655                             if (way.nodes.indexOf(node.id) !== -1) {
53656                                 count += 1;
53657                             }
53658                             if (count > 1) break;
53659                         }
53660
53661                         if (count > 1) {
53662                             sharedActions.push(action);
53663                         } else {
53664                             unsharedActions.push(action);
53665                         }
53666                     }
53667                 });
53668
53669                 _descriptionID += 'no_points.';
53670                 _descriptionID += _wayIDs.length === 1 ? 'single_way.' : 'multiple_ways.';
53671
53672                 if (sharedActions.length) {
53673                     // if any nodes are shared, only disconnect the selected ways from each other
53674                     _actions = sharedActions;
53675                     _descriptionID += 'conjoined';
53676                     _annotationID = 'from_each_other';
53677                 } else {
53678                     // if no nodes are shared, disconnect the selected ways from all connected ways
53679                     _actions = unsharedActions;
53680                     if (_wayIDs.length === 1) {
53681                         _descriptionID += context.graph().geometry(_wayIDs[0]);
53682                     } else {
53683                         _descriptionID += 'separate';
53684                     }
53685                 }
53686             }
53687
53688
53689             var operation = function() {
53690                 context.perform(function(graph) {
53691                     return _actions.reduce(function(graph, action) { return action(graph); }, graph);
53692                 }, operation.annotation());
53693
53694                 context.validator().validate();
53695             };
53696
53697
53698             operation.available = function() {
53699                 if (_actions.length === 0) return false;
53700                 if (_otherIDs.length !== 0) return false;
53701
53702                 if (_vertexIDs.length !== 0 && _wayIDs.length !== 0 && !_wayIDs.every(function(wayID) {
53703                     return _vertexIDs.some(function(vertexID) {
53704                         var way = context.entity(wayID);
53705                         return way.nodes.indexOf(vertexID) !== -1;
53706                     });
53707                 })) return false;
53708
53709                 return true;
53710             };
53711
53712
53713             operation.disabled = function() {
53714                 var reason;
53715                 for (var actionIndex in _actions) {
53716                     reason = _actions[actionIndex].disabled(context.graph());
53717                     if (reason) return reason;
53718                 }
53719
53720                 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
53721                     return 'too_large.' + ((_vertexIDs.length ? _vertexIDs : _wayIDs).length === 1 ? 'single' : 'multiple');
53722                 } else if (_coords && someMissing()) {
53723                     return 'not_downloaded';
53724                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
53725                     return 'connected_to_hidden';
53726                 }
53727
53728                 return false;
53729
53730
53731                 function someMissing() {
53732                     if (context.inIntro()) return false;
53733                     var osm = context.connection();
53734                     if (osm) {
53735                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
53736                         if (missing.length) {
53737                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
53738                             return true;
53739                         }
53740                     }
53741                     return false;
53742                 }
53743             };
53744
53745
53746             operation.tooltip = function() {
53747                 var disable = operation.disabled();
53748                 if (disable) {
53749                     return _t('operations.disconnect.' + disable);
53750                 }
53751                 return _t('operations.disconnect.description.' + _descriptionID);
53752             };
53753
53754
53755             operation.annotation = function() {
53756                 return _t('operations.disconnect.annotation.' + _annotationID);
53757             };
53758
53759
53760             operation.id = 'disconnect';
53761             operation.keys = [_t('operations.disconnect.key')];
53762             operation.title = _t('operations.disconnect.title');
53763             operation.behavior = behaviorOperation(context).which(operation);
53764
53765             return operation;
53766         }
53767
53768         function operationDowngrade(context, selectedIDs) {
53769             var affectedFeatureCount = 0;
53770             var downgradeType;
53771
53772             setDowngradeTypeForEntityIDs();
53773
53774             var multi = affectedFeatureCount === 1 ? 'single' : 'multiple';
53775
53776             function setDowngradeTypeForEntityIDs() {
53777                 for (var i in selectedIDs) {
53778                     var entityID = selectedIDs[i];
53779                     var type = downgradeTypeForEntityID(entityID);
53780                     if (type) {
53781                         affectedFeatureCount += 1;
53782                         if (downgradeType && type !== downgradeType) {
53783                             downgradeType = 'building_address';
53784                         } else {
53785                             downgradeType = type;
53786                         }
53787                     }
53788                 }
53789             }
53790
53791             function downgradeTypeForEntityID(entityID) {
53792                 var graph = context.graph();
53793                 var entity = graph.entity(entityID);
53794                 var preset = _mainPresetIndex.match(entity, graph);
53795
53796                 if (!preset || preset.isFallback()) return null;
53797
53798                 if (entity.type === 'node' &&
53799                     preset.id !== 'address' &&
53800                     Object.keys(entity.tags).some(function(key) {
53801                         return key.match(/^addr:.{1,}/);
53802                     })) {
53803
53804                     return 'address';
53805                 }
53806                 if (entity.geometry(graph) === 'area' &&
53807                     entity.tags.building &&
53808                     !preset.tags.building) {
53809
53810                     return 'building';
53811                 }
53812
53813                 return null;
53814             }
53815
53816             var buildingKeysToKeep = ['architect', 'building', 'height', 'layer', 'source', 'type', 'wheelchair'];
53817             var addressKeysToKeep = ['source'];
53818
53819             var operation = function () {
53820                 context.perform(function(graph) {
53821
53822                     for (var i in selectedIDs) {
53823                         var entityID = selectedIDs[i];
53824                         var type = downgradeTypeForEntityID(entityID);
53825                         if (!type) continue;
53826
53827                         var tags = Object.assign({}, graph.entity(entityID).tags);  // shallow copy
53828                         for (var key in tags) {
53829                             if (type === 'address' && addressKeysToKeep.indexOf(key) !== -1) continue;
53830                             if (type === 'building') {
53831                                 if (buildingKeysToKeep.indexOf(key) !== -1 ||
53832                                     key.match(/^building:.{1,}/) ||
53833                                     key.match(/^roof:.{1,}/)) continue;
53834                             }
53835                             // keep address tags for buildings too
53836                             if (key.match(/^addr:.{1,}/)) continue;
53837
53838                             delete tags[key];
53839                         }
53840                         graph = actionChangeTags(entityID, tags)(graph);
53841                     }
53842                     return graph;
53843                 }, operation.annotation());
53844
53845                 context.validator().validate();
53846
53847                 // refresh the select mode to enable the delete operation
53848                 context.enter(modeSelect(context, selectedIDs));
53849             };
53850
53851
53852             operation.available = function () {
53853                 return downgradeType;
53854             };
53855
53856
53857             operation.disabled = function () {
53858                 if (selectedIDs.some(hasWikidataTag)) {
53859                     return 'has_wikidata_tag';
53860                 }
53861                 return false;
53862
53863                 function hasWikidataTag(id) {
53864                     var entity = context.entity(id);
53865                     return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
53866                 }
53867             };
53868
53869
53870             operation.tooltip = function () {
53871                 var disable = operation.disabled();
53872                 return disable ?
53873                     _t('operations.downgrade.' + disable + '.' + multi) :
53874                     _t('operations.downgrade.description.' + downgradeType);
53875             };
53876
53877
53878             operation.annotation = function () {
53879                 var suffix;
53880                 if (downgradeType === 'building_address') {
53881                     suffix = 'multiple';
53882                 } else {
53883                     suffix = downgradeType + '.' + multi;
53884                 }
53885                 return _t('operations.downgrade.annotation.' + suffix, { n: affectedFeatureCount});
53886             };
53887
53888
53889             operation.id = 'downgrade';
53890             operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
53891             operation.title = _t('operations.downgrade.title');
53892             operation.behavior = behaviorOperation(context).which(operation);
53893
53894
53895             return operation;
53896         }
53897
53898         function operationExtract(context, selectedIDs) {
53899
53900             var _amount = selectedIDs.length === 1 ? 'single' : 'multiple';
53901             var _geometries = utilArrayUniq(selectedIDs.map(function(entityID) {
53902                 return context.graph().hasEntity(entityID) && context.graph().geometry(entityID);
53903             }).filter(Boolean));
53904             var _geometryID = _geometries.length === 1 ? _geometries[0] : 'feature';
53905
53906             var _extent;
53907             var _actions = selectedIDs.map(function(entityID) {
53908                 var graph = context.graph();
53909                 var entity = graph.hasEntity(entityID);
53910                 if (!entity || !entity.hasInterestingTags()) return;
53911
53912                 if (entity.type === 'node' && graph.parentWays(entity).length === 0) return;
53913
53914                 if (entity.type !== 'node') {
53915                     var preset = _mainPresetIndex.match(entity, graph);
53916                     // only allow extraction from ways/relations if the preset supports points
53917                     if (preset.geometry.indexOf('point') === -1) return;
53918                 }
53919
53920                 _extent = _extent ? _extent.extend(entity.extent(graph)) : entity.extent(graph);
53921
53922                 return actionExtract(entityID);
53923             }).filter(Boolean);
53924
53925
53926             var operation = function () {
53927                 var combinedAction = function(graph) {
53928                     _actions.forEach(function(action) {
53929                         graph = action(graph);
53930                     });
53931                     return graph;
53932                 };
53933                 context.perform(combinedAction, operation.annotation());  // do the extract
53934
53935                 var extractedNodeIDs = _actions.map(function(action) {
53936                     return action.getExtractedNodeID();
53937                 });
53938                 context.enter(modeSelect(context, extractedNodeIDs));
53939             };
53940
53941
53942             operation.available = function () {
53943                 return _actions.length && selectedIDs.length === _actions.length;
53944             };
53945
53946
53947             operation.disabled = function () {
53948
53949                 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
53950                     return 'too_large';
53951                 } else if (selectedIDs.some(function(entityID) {
53952                     return context.graph().geometry(entityID) === 'vertex' && context.hasHiddenConnections(entityID);
53953                 })) {
53954                     return 'connected_to_hidden';
53955                 }
53956
53957                 return false;
53958             };
53959
53960
53961             operation.tooltip = function () {
53962                 var disableReason = operation.disabled();
53963                 if (disableReason) {
53964                     return _t('operations.extract.' + disableReason + '.' + _amount);
53965                 } else {
53966                     return _t('operations.extract.description.' + _geometryID + '.' + _amount);
53967                 }
53968             };
53969
53970
53971             operation.annotation = function () {
53972                 return _t('operations.extract.annotation.' + _amount, { n: selectedIDs.length });
53973             };
53974
53975
53976             operation.id = 'extract';
53977             operation.keys = [_t('operations.extract.key')];
53978             operation.title = _t('operations.extract.title');
53979             operation.behavior = behaviorOperation(context).which(operation);
53980
53981
53982             return operation;
53983         }
53984
53985         function operationMerge(context, selectedIDs) {
53986
53987             var _action = getAction();
53988
53989             function getAction() {
53990                 // prefer a non-disabled action first
53991                 var join = actionJoin(selectedIDs);
53992                 if (!join.disabled(context.graph())) return join;
53993
53994                 var merge = actionMerge(selectedIDs);
53995                 if (!merge.disabled(context.graph())) return merge;
53996
53997                 var mergePolygon = actionMergePolygon(selectedIDs);
53998                 if (!mergePolygon.disabled(context.graph())) return mergePolygon;
53999
54000                 var mergeNodes = actionMergeNodes(selectedIDs);
54001                 if (!mergeNodes.disabled(context.graph())) return mergeNodes;
54002
54003                 // otherwise prefer an action with an interesting disabled reason
54004                 if (join.disabled(context.graph()) !== 'not_eligible') return join;
54005                 if (merge.disabled(context.graph()) !== 'not_eligible') return merge;
54006                 if (mergePolygon.disabled(context.graph()) !== 'not_eligible') return mergePolygon;
54007
54008                 return mergeNodes;
54009             }
54010
54011             var operation = function() {
54012
54013                 if (operation.disabled()) return;
54014
54015                 context.perform(_action, operation.annotation());
54016
54017                 context.validator().validate();
54018
54019                 var resultIDs = selectedIDs.filter(context.hasEntity);
54020                 if (resultIDs.length > 1) {
54021                     var interestingIDs = resultIDs.filter(function(id) {
54022                         return context.entity(id).hasInterestingTags();
54023                     });
54024                     if (interestingIDs.length) resultIDs = interestingIDs;
54025                 }
54026                 context.enter(modeSelect(context, resultIDs));
54027             };
54028
54029             operation.available = function() {
54030                 return selectedIDs.length >= 2;
54031             };
54032
54033             operation.disabled = function() {
54034                 var actionDisabled = _action.disabled(context.graph());
54035                 if (actionDisabled) return actionDisabled;
54036
54037                 var osm = context.connection();
54038                 if (osm &&
54039                     _action.resultingWayNodesLength &&
54040                     _action.resultingWayNodesLength(context.graph()) > osm.maxWayNodes()) {
54041                     return 'too_many_vertices';
54042                 }
54043
54044                 return false;
54045             };
54046
54047             operation.tooltip = function() {
54048                 var disabled = operation.disabled();
54049                 if (disabled) {
54050                     if (disabled === 'restriction') {
54051                         return _t('operations.merge.restriction',
54052                             { relation: _mainPresetIndex.item('type/restriction').name() });
54053                     }
54054                     return _t('operations.merge.' + disabled);
54055                 }
54056                 return _t('operations.merge.description');
54057             };
54058
54059             operation.annotation = function() {
54060                 return _t('operations.merge.annotation', { n: selectedIDs.length });
54061             };
54062
54063             operation.id = 'merge';
54064             operation.keys = [_t('operations.merge.key')];
54065             operation.title = _t('operations.merge.title');
54066             operation.behavior = behaviorOperation(context).which(operation);
54067
54068             return operation;
54069         }
54070
54071         // see also `behaviorPaste`
54072         function operationPaste(context) {
54073
54074             var _pastePoint;
54075
54076             var operation = function() {
54077
54078                 if (!_pastePoint) return;
54079
54080                 var oldIDs = context.copyIDs();
54081                 if (!oldIDs.length) return;
54082
54083                 var projection = context.projection;
54084                 var extent = geoExtent();
54085                 var oldGraph = context.copyGraph();
54086                 var newIDs = [];
54087
54088                 var action = actionCopyEntities(oldIDs, oldGraph);
54089                 context.perform(action);
54090
54091                 var copies = action.copies();
54092                 var originals = new Set();
54093                 Object.values(copies).forEach(function(entity) { originals.add(entity.id); });
54094
54095                 for (var id in copies) {
54096                     var oldEntity = oldGraph.entity(id);
54097                     var newEntity = copies[id];
54098
54099                     extent._extend(oldEntity.extent(oldGraph));
54100
54101                     // Exclude child nodes from newIDs if their parent way was also copied.
54102                     var parents = context.graph().parentWays(newEntity);
54103                     var parentCopied = parents.some(function(parent) {
54104                         return originals.has(parent.id);
54105                     });
54106
54107                     if (!parentCopied) {
54108                         newIDs.push(newEntity.id);
54109                     }
54110                 }
54111
54112                 // Use the location of the copy operation to offset the paste location,
54113                 // or else use the center of the pasted extent
54114                 var copyPoint = (context.copyLonLat() && projection(context.copyLonLat())) ||
54115                     projection(extent.center());
54116                 var delta = geoVecSubtract(_pastePoint, copyPoint);
54117
54118                 // Move the pasted objects to be anchored at the paste location
54119                 context.replace(actionMove(newIDs, delta, projection), operation.annotation());
54120                 context.enter(modeSelect(context, newIDs));
54121             };
54122
54123             operation.point = function(val) {
54124                 _pastePoint = val;
54125                 return operation;
54126             };
54127
54128             operation.available = function() {
54129                 return context.mode().id === 'browse';
54130             };
54131
54132             operation.disabled = function() {
54133                 return !context.copyIDs().length;
54134             };
54135
54136             operation.tooltip = function() {
54137                 var oldGraph = context.copyGraph();
54138                 var ids = context.copyIDs();
54139                 if (!ids.length) {
54140                     return _t('operations.paste.nothing_copied');
54141                 }
54142                 return ids.length === 1 ?
54143                     _t('operations.paste.description.single', { feature: utilDisplayLabel(oldGraph.entity(ids[0]), oldGraph) }) :
54144                     _t('operations.paste.description.multiple', { n: ids.length.toString() });
54145             };
54146
54147             operation.annotation = function() {
54148                 var ids = context.copyIDs();
54149                 return ids.length === 1 ?
54150                     _t('operations.paste.annotation.single') :
54151                     _t('operations.paste.annotation.multiple', { n: ids.length.toString() });
54152             };
54153
54154             operation.id = 'paste';
54155             operation.keys = [uiCmd('⌘V')];
54156             operation.title = _t('operations.paste.title');
54157
54158             return operation;
54159         }
54160
54161         function operationReverse(context, selectedIDs) {
54162
54163             var operation = function() {
54164                 context.perform(function combinedReverseAction(graph) {
54165                     actions().forEach(function(action) {
54166                         graph = action(graph);
54167                     });
54168                     return graph;
54169                 }, operation.annotation());
54170                 context.validator().validate();
54171             };
54172
54173             function actions(situation) {
54174                 return selectedIDs.map(function(entityID) {
54175                     var entity = context.hasEntity(entityID);
54176                     if (!entity) return;
54177
54178                     if (situation === 'toolbar') {
54179                         if (entity.type === 'way' &&
54180                             (!entity.isOneWay() && !entity.isSided())) return;
54181                     }
54182
54183                     var geometry = entity.geometry(context.graph());
54184                     if (entity.type !== 'node' && geometry !== 'line') return;
54185
54186                     var action = actionReverse(entityID);
54187                     if (action.disabled(context.graph())) return;
54188
54189                     return action;
54190                 }).filter(Boolean);
54191             }
54192
54193             function reverseTypeID() {
54194                 var acts = actions();
54195                 var nodeActionCount = acts.filter(function(act) {
54196                     var entity = context.hasEntity(act.entityID());
54197                     return entity && entity.type === 'node';
54198                 }).length;
54199                 var typeID = nodeActionCount === 0 ? 'line' : (nodeActionCount === acts.length ? 'point' : 'features');
54200                 if (typeID !== 'features' && acts.length > 1) typeID += 's';
54201                 return typeID;
54202             }
54203
54204
54205             operation.available = function(situation) {
54206                 return actions(situation).length > 0;
54207             };
54208
54209
54210             operation.disabled = function() {
54211                 return false;
54212             };
54213
54214
54215             operation.tooltip = function() {
54216                 return _t('operations.reverse.description.' + reverseTypeID());
54217             };
54218
54219
54220             operation.annotation = function() {
54221                 return _t('operations.reverse.annotation.' + reverseTypeID());
54222             };
54223
54224
54225             operation.id = 'reverse';
54226             operation.keys = [_t('operations.reverse.key')];
54227             operation.title = _t('operations.reverse.title');
54228             operation.behavior = behaviorOperation(context).which(operation);
54229
54230             return operation;
54231         }
54232
54233         function operationSplit(context, selectedIDs) {
54234             var vertices = selectedIDs
54235                 .filter(function(id) { return context.graph().geometry(id) === 'vertex'; });
54236             var entityID = vertices[0];
54237             var action = actionSplit(entityID);
54238             var ways = [];
54239
54240             if (vertices.length === 1) {
54241                 if (entityID && selectedIDs.length > 1) {
54242                     var ids = selectedIDs.filter(function(id) { return id !== entityID; });
54243                     action.limitWays(ids);
54244                 }
54245                 ways = action.ways(context.graph());
54246             }
54247
54248
54249             var operation = function() {
54250                 var difference = context.perform(action, operation.annotation());
54251                 context.enter(modeSelect(context, difference.extantIDs()));
54252             };
54253
54254
54255             operation.available = function() {
54256                 return vertices.length === 1;
54257             };
54258
54259
54260             operation.disabled = function() {
54261                 var reason = action.disabled(context.graph());
54262                 if (reason) {
54263                     return reason;
54264                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
54265                     return 'connected_to_hidden';
54266                 }
54267
54268                 return false;
54269             };
54270
54271
54272             operation.tooltip = function() {
54273                 var disable = operation.disabled();
54274                 if (disable) {
54275                     return _t('operations.split.' + disable);
54276                 } else if (ways.length === 1) {
54277                     return _t('operations.split.description.' + context.graph().geometry(ways[0].id));
54278                 } else {
54279                     return _t('operations.split.description.multiple');
54280                 }
54281             };
54282
54283
54284             operation.annotation = function() {
54285                 return ways.length === 1 ?
54286                     _t('operations.split.annotation.' + context.graph().geometry(ways[0].id)) :
54287                     _t('operations.split.annotation.multiple', { n: ways.length });
54288             };
54289
54290
54291             operation.id = 'split';
54292             operation.keys = [_t('operations.split.key')];
54293             operation.title = _t('operations.split.title');
54294             operation.behavior = behaviorOperation(context).which(operation);
54295
54296             return operation;
54297         }
54298
54299         function operationStraighten(context, selectedIDs) {
54300             var _wayIDs = selectedIDs.filter(function(id) { return id.charAt(0) === 'w'; });
54301             var _nodeIDs = selectedIDs.filter(function(id) { return id.charAt(0) === 'n'; });
54302             var _amount = ((_wayIDs.length ? _wayIDs : _nodeIDs).length === 1 ? 'single' : 'multiple');
54303
54304             var _nodes = utilGetAllNodes(selectedIDs, context.graph());
54305             var _coords = _nodes.map(function(n) { return n.loc; });
54306             var _extent = utilTotalExtent(selectedIDs, context.graph());
54307             var _action = chooseAction();
54308             var _geometry;
54309
54310
54311             function chooseAction() {
54312                 // straighten selected nodes
54313                 if (_wayIDs.length === 0 && _nodeIDs.length > 2) {
54314                     _geometry = 'points';
54315                     return actionStraightenNodes(_nodeIDs, context.projection);
54316
54317                 // straighten selected ways (possibly between range of 2 selected nodes)
54318                 } else if (_wayIDs.length > 0 && (_nodeIDs.length === 0 || _nodeIDs.length === 2)) {
54319                     var startNodeIDs = [];
54320                     var endNodeIDs = [];
54321
54322                     for (var i = 0; i < selectedIDs.length; i++) {
54323                         var entity = context.entity(selectedIDs[i]);
54324                         if (entity.type === 'node') {
54325                             continue;
54326                         } else if (entity.type !== 'way' || entity.isClosed()) {
54327                             return null;  // exit early, can't straighten these
54328                         }
54329
54330                         startNodeIDs.push(entity.first());
54331                         endNodeIDs.push(entity.last());
54332                     }
54333
54334                     // Remove duplicate end/startNodeIDs (duplicate nodes cannot be at the line end)
54335                     startNodeIDs = startNodeIDs.filter(function(n) {
54336                         return startNodeIDs.indexOf(n) === startNodeIDs.lastIndexOf(n);
54337                     });
54338                     endNodeIDs = endNodeIDs.filter(function(n) {
54339                         return endNodeIDs.indexOf(n) === endNodeIDs.lastIndexOf(n);
54340                     });
54341
54342                     // Ensure all ways are connected (i.e. only 2 unique endpoints/startpoints)
54343                     if (utilArrayDifference(startNodeIDs, endNodeIDs).length +
54344                         utilArrayDifference(endNodeIDs, startNodeIDs).length !== 2) return null;
54345
54346                     // Ensure path contains at least 3 unique nodes
54347                     var wayNodeIDs = utilGetAllNodes(_wayIDs, context.graph())
54348                         .map(function(node) { return node.id; });
54349                     if (wayNodeIDs.length <= 2) return null;
54350
54351                     // If range of 2 selected nodes is supplied, ensure nodes lie on the selected path
54352                     if (_nodeIDs.length === 2 && (
54353                         wayNodeIDs.indexOf(_nodeIDs[0]) === -1 || wayNodeIDs.indexOf(_nodeIDs[1]) === -1
54354                     )) return null;
54355
54356                     if (_nodeIDs.length) {
54357                         // If we're only straightenting between two points, we only need that extent visible
54358                         _extent = utilTotalExtent(_nodeIDs, context.graph());
54359                     }
54360
54361                     _geometry = _wayIDs.length === 1 ? 'line' : 'lines';
54362                     return actionStraightenWay(selectedIDs, context.projection);
54363                 }
54364
54365                 return null;
54366             }
54367
54368
54369             function operation() {
54370                 if (!_action) return;
54371
54372                 context.perform(_action, operation.annotation());
54373
54374                 window.setTimeout(function() {
54375                     context.validator().validate();
54376                 }, 300);  // after any transition
54377             }
54378
54379
54380             operation.available = function() {
54381                 return Boolean(_action);
54382             };
54383
54384
54385             operation.disabled = function() {
54386                 var reason = _action.disabled(context.graph());
54387                 if (reason) {
54388                     return reason;
54389                 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
54390                     return 'too_large';
54391                 } else if (someMissing()) {
54392                     return 'not_downloaded';
54393                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
54394                     return 'connected_to_hidden';
54395                 }
54396
54397                 return false;
54398
54399
54400                 function someMissing() {
54401                     if (context.inIntro()) return false;
54402                     var osm = context.connection();
54403                     if (osm) {
54404                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
54405                         if (missing.length) {
54406                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
54407                             return true;
54408                         }
54409                     }
54410                     return false;
54411                 }
54412             };
54413
54414
54415             operation.tooltip = function() {
54416                 var disable = operation.disabled();
54417                 return disable ?
54418                     _t('operations.straighten.' + disable + '.' + _amount) :
54419                     _t('operations.straighten.description.' + _geometry);
54420             };
54421
54422
54423             operation.annotation = function() {
54424                 return _t('operations.straighten.annotation.' + _geometry);
54425             };
54426
54427
54428             operation.id = 'straighten';
54429             operation.keys = [_t('operations.straighten.key')];
54430             operation.title = _t('operations.straighten.title');
54431             operation.behavior = behaviorOperation(context).which(operation);
54432
54433             return operation;
54434         }
54435
54436         var Operations = /*#__PURE__*/Object.freeze({
54437                 __proto__: null,
54438                 operationCircularize: operationCircularize,
54439                 operationContinue: operationContinue,
54440                 operationCopy: operationCopy,
54441                 operationDelete: operationDelete,
54442                 operationDisconnect: operationDisconnect,
54443                 operationDowngrade: operationDowngrade,
54444                 operationExtract: operationExtract,
54445                 operationMerge: operationMerge,
54446                 operationMove: operationMove,
54447                 operationOrthogonalize: operationOrthogonalize,
54448                 operationPaste: operationPaste,
54449                 operationReflectShort: operationReflectShort,
54450                 operationReflectLong: operationReflectLong,
54451                 operationReverse: operationReverse,
54452                 operationRotate: operationRotate,
54453                 operationSplit: operationSplit,
54454                 operationStraighten: operationStraighten
54455         });
54456
54457         var _relatedParent;
54458
54459
54460         function modeSelect(context, selectedIDs) {
54461             var mode = {
54462                 id: 'select',
54463                 button: 'browse'
54464             };
54465
54466             var keybinding = utilKeybinding('select');
54467
54468             var _breatheBehavior = behaviorBreathe();
54469             var _modeDragNode = modeDragNode(context);
54470             var _selectBehavior;
54471             var _behaviors = [];
54472
54473             var _operations = [];
54474             var _newFeature = false;
54475             var _follow = false;
54476
54477
54478             function singular() {
54479                 if (selectedIDs && selectedIDs.length === 1) {
54480                     return context.hasEntity(selectedIDs[0]);
54481                 }
54482             }
54483
54484             function selectedEntities() {
54485                 return selectedIDs.map(function(id) {
54486                     return context.hasEntity(id);
54487                 }).filter(Boolean);
54488             }
54489
54490
54491             function checkSelectedIDs() {
54492                 var ids = [];
54493                 if (Array.isArray(selectedIDs)) {
54494                     ids = selectedIDs.filter(function(id) {
54495                         return context.hasEntity(id);
54496                     });
54497                 }
54498
54499                 if (!ids.length) {
54500                     context.enter(modeBrowse(context));
54501                     return false;
54502                 } else if ((selectedIDs.length > 1 && ids.length === 1) ||
54503                     (selectedIDs.length === 1 && ids.length > 1)) {
54504                     // switch between single- and multi-select UI
54505                     context.enter(modeSelect(context, ids));
54506                     return false;
54507                 }
54508
54509                 selectedIDs = ids;
54510                 return true;
54511             }
54512
54513
54514             // find the common parent ways for nextVertex, previousVertex
54515             function commonParents() {
54516                 var graph = context.graph();
54517                 var commonParents = [];
54518
54519                 for (var i = 0; i < selectedIDs.length; i++) {
54520                     var entity = context.hasEntity(selectedIDs[i]);
54521                     if (!entity || entity.geometry(graph) !== 'vertex') {
54522                         return [];  // selection includes some not vertexes
54523                     }
54524
54525                     var currParents = graph.parentWays(entity).map(function(w) { return w.id; });
54526                     if (!commonParents.length) {
54527                         commonParents = currParents;
54528                         continue;
54529                     }
54530
54531                     commonParents = utilArrayIntersection(commonParents, currParents);
54532                     if (!commonParents.length) {
54533                         return [];
54534                     }
54535                 }
54536
54537                 return commonParents;
54538             }
54539
54540
54541             function singularParent() {
54542                 var parents = commonParents();
54543                 if (!parents || parents.length === 0) {
54544                     _relatedParent = null;
54545                     return null;
54546                 }
54547
54548                 // relatedParent is used when we visit a vertex with multiple
54549                 // parents, and we want to remember which parent line we started on.
54550
54551                 if (parents.length === 1) {
54552                     _relatedParent = parents[0];  // remember this parent for later
54553                     return _relatedParent;
54554                 }
54555
54556                 if (parents.indexOf(_relatedParent) !== -1) {
54557                     return _relatedParent;   // prefer the previously seen parent
54558                 }
54559
54560                 return parents[0];
54561             }
54562
54563
54564             mode.selectedIDs = function(val) {
54565                 if (!arguments.length) return selectedIDs;
54566                 selectedIDs = val;
54567                 return mode;
54568             };
54569
54570
54571             mode.zoomToSelected = function() {
54572                 context.map().zoomToEase(selectedEntities());
54573             };
54574
54575
54576             mode.newFeature = function(val) {
54577                 if (!arguments.length) return _newFeature;
54578                 _newFeature = val;
54579                 return mode;
54580             };
54581
54582
54583             mode.selectBehavior = function(val) {
54584                 if (!arguments.length) return _selectBehavior;
54585                 _selectBehavior = val;
54586                 return mode;
54587             };
54588
54589
54590             mode.follow = function(val) {
54591                 if (!arguments.length) return _follow;
54592                 _follow = val;
54593                 return mode;
54594             };
54595
54596             function loadOperations() {
54597
54598                 _operations.forEach(function(operation) {
54599                     if (operation.behavior) {
54600                         context.uninstall(operation.behavior);
54601                     }
54602                 });
54603
54604                 _operations = Object.values(Operations)
54605                     .map(function(o) { return o(context, selectedIDs); })
54606                     .filter(function(o) { return o.available() && o.id !== 'delete' && o.id !== 'downgrade' && o.id !== 'copy'; });
54607
54608                 var copyOperation = operationCopy(context, selectedIDs);
54609                 if (copyOperation.available()) {
54610                     // group copy operation with delete/downgrade
54611                     _operations.push(copyOperation);
54612                 }
54613
54614                 var downgradeOperation = operationDowngrade(context, selectedIDs);
54615                 // don't allow delete if downgrade is available
54616                 var lastOperation = !context.inIntro() && downgradeOperation.available() ? downgradeOperation : operationDelete(context, selectedIDs);
54617
54618                 _operations.push(lastOperation);
54619
54620                 _operations.forEach(function(operation) {
54621                     if (operation.behavior) {
54622                         context.install(operation.behavior);
54623                     }
54624                 });
54625
54626                 // remove any displayed menu
54627                 context.ui().closeEditMenu();
54628             }
54629
54630             mode.operations = function() {
54631                 return _operations;
54632             };
54633
54634
54635             mode.enter = function() {
54636                 if (!checkSelectedIDs()) return;
54637
54638                 context.features().forceVisible(selectedIDs);
54639
54640                 _modeDragNode.restoreSelectedIDs(selectedIDs);
54641
54642                 loadOperations();
54643
54644                 if (!_behaviors.length) {
54645                     if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
54646
54647                     _behaviors = [
54648                         behaviorPaste(context),
54649                         _breatheBehavior,
54650                         behaviorHover(context).on('hover', context.ui().sidebar.hoverModeSelect),
54651                         _selectBehavior,
54652                         behaviorLasso(context),
54653                         _modeDragNode.behavior,
54654                         modeDragNote(context).behavior
54655                     ];
54656                 }
54657                 _behaviors.forEach(context.install);
54658
54659                 keybinding
54660                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
54661                     .on(['[', 'pgup'], previousVertex)
54662                     .on([']', 'pgdown'], nextVertex)
54663                     .on(['{', uiCmd('⌘['), 'home'], firstVertex)
54664                     .on(['}', uiCmd('⌘]'), 'end'], lastVertex)
54665                     .on(uiCmd('⇧←'), nudgeSelection([-10, 0]))
54666                     .on(uiCmd('⇧↑'), nudgeSelection([0, -10]))
54667                     .on(uiCmd('⇧→'), nudgeSelection([10, 0]))
54668                     .on(uiCmd('⇧↓'), nudgeSelection([0, 10]))
54669                     .on(uiCmd('⇧⌘←'), nudgeSelection([-100, 0]))
54670                     .on(uiCmd('⇧⌘↑'), nudgeSelection([0, -100]))
54671                     .on(uiCmd('⇧⌘→'), nudgeSelection([100, 0]))
54672                     .on(uiCmd('⇧⌘↓'), nudgeSelection([0, 100]))
54673                     .on(['\\', 'pause'], nextParent)
54674                     .on('⎋', esc, true);
54675
54676                 select(document)
54677                     .call(keybinding);
54678
54679                 context.ui().sidebar
54680                     .select(selectedIDs, _newFeature);
54681
54682                 context.history()
54683                     .on('change.select', function() {
54684                         loadOperations();
54685                         // reselect after change in case relation members were removed or added
54686                         selectElements();
54687                     })
54688                     .on('undone.select', checkSelectedIDs)
54689                     .on('redone.select', checkSelectedIDs);
54690
54691                 context.map()
54692                     .on('drawn.select', selectElements)
54693                     .on('crossEditableZoom.select', function() {
54694                         selectElements();
54695                         _breatheBehavior.restartIfNeeded(context.surface());
54696                     });
54697
54698                 context.map().doubleUpHandler()
54699                     .on('doubleUp.modeSelect', didDoubleUp);
54700
54701
54702                 selectElements();
54703
54704                 if (_follow) {
54705                     var extent = geoExtent();
54706                     var graph = context.graph();
54707                     selectedIDs.forEach(function(id) {
54708                         var entity = context.entity(id);
54709                         extent._extend(entity.extent(graph));
54710                     });
54711
54712                     var loc = extent.center();
54713                     context.map().centerEase(loc);
54714                 }
54715
54716
54717                 function nudgeSelection(delta) {
54718                     return function() {
54719                         // prevent nudging during low zoom selection
54720                         if (!context.map().withinEditableZoom()) return;
54721
54722                         var moveOp = operationMove(context, selectedIDs);
54723                         if (moveOp.disabled()) {
54724                             context.ui().flash
54725                                 .duration(4000)
54726                                 .iconName('#iD-operation-' + moveOp.id)
54727                                 .iconClass('operation disabled')
54728                                 .text(moveOp.tooltip)();
54729                         } else {
54730                             context.perform(actionMove(selectedIDs, delta, context.projection), moveOp.annotation());
54731                         }
54732                     };
54733                 }
54734
54735
54736                 function didDoubleUp(loc) {
54737                     if (!context.map().withinEditableZoom()) return;
54738
54739                     var target = select(event.target);
54740
54741                     var datum = target.datum();
54742                     var entity = datum && datum.properties && datum.properties.entity;
54743                     if (!entity) return;
54744
54745                     if (entity instanceof osmWay && target.classed('target')) {
54746                         var choice = geoChooseEdge(context.graph().childNodes(entity), loc, context.projection);
54747                         var prev = entity.nodes[choice.index - 1];
54748                         var next = entity.nodes[choice.index];
54749
54750                         context.perform(
54751                             actionAddMidpoint({ loc: choice.loc, edge: [prev, next] }, osmNode()),
54752                             _t('operations.add.annotation.vertex')
54753                         );
54754
54755                     } else if (entity.type === 'midpoint') {
54756                         context.perform(
54757                             actionAddMidpoint({ loc: entity.loc, edge: entity.edge }, osmNode()),
54758                             _t('operations.add.annotation.vertex'));
54759                     }
54760                 }
54761
54762
54763                 function selectElements() {
54764                     if (!checkSelectedIDs()) return;
54765
54766                     var surface = context.surface();
54767
54768                     surface.selectAll('.selected-member')
54769                         .classed('selected-member', false);
54770
54771                     surface.selectAll('.selected')
54772                         .classed('selected', false);
54773
54774                     surface.selectAll('.related')
54775                         .classed('related', false);
54776
54777                     singularParent();
54778                     if (_relatedParent) {
54779                         surface.selectAll(utilEntitySelector([_relatedParent]))
54780                             .classed('related', true);
54781                     }
54782
54783                     if (context.map().withinEditableZoom()) {
54784                         // Apply selection styling if not in wide selection
54785
54786                         surface
54787                             .selectAll(utilDeepMemberSelector(selectedIDs, context.graph(), true /* skipMultipolgonMembers */))
54788                             .classed('selected-member', true);
54789                         surface
54790                             .selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph()))
54791                             .classed('selected', true);
54792                     }
54793
54794                 }
54795
54796
54797                 function esc() {
54798                     if (context.container().select('.combobox').size()) return;
54799                     context.enter(modeBrowse(context));
54800                 }
54801
54802
54803                 function firstVertex() {
54804                     event.preventDefault();
54805                     var entity = singular();
54806                     var parent = singularParent();
54807                     var way;
54808
54809                     if (entity && entity.type === 'way') {
54810                         way = entity;
54811                     } else if (parent) {
54812                         way = context.entity(parent);
54813                     }
54814
54815                     if (way) {
54816                         context.enter(
54817                             modeSelect(context, [way.first()]).follow(true)
54818                         );
54819                     }
54820                 }
54821
54822
54823                 function lastVertex() {
54824                     event.preventDefault();
54825                     var entity = singular();
54826                     var parent = singularParent();
54827                     var way;
54828
54829                     if (entity && entity.type === 'way') {
54830                         way = entity;
54831                     } else if (parent) {
54832                         way = context.entity(parent);
54833                     }
54834
54835                     if (way) {
54836                         context.enter(
54837                             modeSelect(context, [way.last()]).follow(true)
54838                         );
54839                     }
54840                 }
54841
54842
54843                 function previousVertex() {
54844                     event.preventDefault();
54845                     var parent = singularParent();
54846                     if (!parent) return;
54847
54848                     var way = context.entity(parent);
54849                     var length = way.nodes.length;
54850                     var curr = way.nodes.indexOf(selectedIDs[0]);
54851                     var index = -1;
54852
54853                     if (curr > 0) {
54854                         index = curr - 1;
54855                     } else if (way.isClosed()) {
54856                         index = length - 2;
54857                     }
54858
54859                     if (index !== -1) {
54860                         context.enter(
54861                             modeSelect(context, [way.nodes[index]]).follow(true)
54862                         );
54863                     }
54864                 }
54865
54866
54867                 function nextVertex() {
54868                     event.preventDefault();
54869                     var parent = singularParent();
54870                     if (!parent) return;
54871
54872                     var way = context.entity(parent);
54873                     var length = way.nodes.length;
54874                     var curr = way.nodes.indexOf(selectedIDs[0]);
54875                     var index = -1;
54876
54877                     if (curr < length - 1) {
54878                         index = curr + 1;
54879                     } else if (way.isClosed()) {
54880                         index = 0;
54881                     }
54882
54883                     if (index !== -1) {
54884                         context.enter(
54885                             modeSelect(context, [way.nodes[index]]).follow(true)
54886                         );
54887                     }
54888                 }
54889
54890
54891                 function nextParent() {
54892                     event.preventDefault();
54893                     var parents = commonParents();
54894                     if (!parents || parents.length < 2) return;
54895
54896                     var index = parents.indexOf(_relatedParent);
54897                     if (index < 0 || index > parents.length - 2) {
54898                         _relatedParent = parents[0];
54899                     } else {
54900                         _relatedParent = parents[index + 1];
54901                     }
54902
54903                     var surface = context.surface();
54904                     surface.selectAll('.related')
54905                         .classed('related', false);
54906
54907                     if (_relatedParent) {
54908                         surface.selectAll(utilEntitySelector([_relatedParent]))
54909                             .classed('related', true);
54910                     }
54911                 }
54912             };
54913
54914
54915             mode.exit = function() {
54916
54917                 _newFeature = false;
54918
54919                 _operations.forEach(function(operation) {
54920                     if (operation.behavior) {
54921                         context.uninstall(operation.behavior);
54922                     }
54923                 });
54924                 _operations = [];
54925
54926                 _behaviors.forEach(context.uninstall);
54927
54928                 select(document)
54929                     .call(keybinding.unbind);
54930
54931                 context.ui().closeEditMenu();
54932
54933                 context.history()
54934                     .on('change.select', null)
54935                     .on('undone.select', null)
54936                     .on('redone.select', null);
54937
54938                 var surface = context.surface();
54939
54940                 surface
54941                     .selectAll('.selected-member')
54942                     .classed('selected-member', false);
54943
54944                 surface
54945                     .selectAll('.selected')
54946                     .classed('selected', false);
54947
54948                 surface
54949                     .selectAll('.highlighted')
54950                     .classed('highlighted', false);
54951
54952                 surface
54953                     .selectAll('.related')
54954                     .classed('related', false);
54955
54956                 context.map().on('drawn.select', null);
54957                 context.ui().sidebar.hide();
54958                 context.features().forceVisible([]);
54959
54960                 var entity = singular();
54961                 if (_newFeature && entity && entity.type === 'relation' &&
54962                     // no tags
54963                     Object.keys(entity.tags).length === 0 &&
54964                     // no parent relations
54965                     context.graph().parentRelations(entity).length === 0 &&
54966                     // no members or one member with no role
54967                     (entity.members.length === 0 || (entity.members.length === 1 && !entity.members[0].role))
54968                 ) {
54969                     // the user added this relation but didn't edit it at all, so just delete it
54970                     var deleteAction = actionDeleteRelation(entity.id, true /* don't delete untagged members */);
54971                     context.perform(deleteAction, _t('operations.delete.annotation.relation'));
54972                 }
54973             };
54974
54975
54976             return mode;
54977         }
54978
54979         function uiLasso(context) {
54980             var group, polygon;
54981
54982             lasso.coordinates = [];
54983
54984             function lasso(selection) {
54985                 context.container()
54986                     .classed('lasso', true);
54987
54988                 group = selection
54989                     .append('g')
54990                     .attr('class', 'lasso hide');
54991
54992                 polygon = group
54993                     .append('path')
54994                     .attr('class', 'lasso-path');
54995
54996                 group
54997                     .call(uiToggle(true));
54998             }
54999
55000
55001             function draw() {
55002                 if (polygon) {
55003                     polygon.data([lasso.coordinates])
55004                         .attr('d', function(d) { return 'M' + d.join(' L') + ' Z'; });
55005                 }
55006             }
55007
55008
55009             lasso.extent = function () {
55010                 return lasso.coordinates.reduce(function(extent, point) {
55011                     return extent.extend(geoExtent(point));
55012                 }, geoExtent());
55013             };
55014
55015
55016             lasso.p = function(_) {
55017                 if (!arguments.length) return lasso;
55018                 lasso.coordinates.push(_);
55019                 draw();
55020                 return lasso;
55021             };
55022
55023
55024             lasso.close = function() {
55025                 if (group) {
55026                     group.call(uiToggle(false, function() {
55027                         select(this).remove();
55028                     }));
55029                 }
55030                 context.container().classed('lasso', false);
55031             };
55032
55033
55034             return lasso;
55035         }
55036
55037         function behaviorLasso(context) {
55038
55039             // use pointer events on supported platforms; fallback to mouse events
55040             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
55041
55042             var behavior = function(selection) {
55043                 var lasso;
55044
55045
55046                 function pointerdown() {
55047                     var button = 0;  // left
55048                     if (event.button === button && event.shiftKey === true) {
55049                         lasso = null;
55050
55051                         select(window)
55052                             .on(_pointerPrefix + 'move.lasso', pointermove)
55053                             .on(_pointerPrefix + 'up.lasso', pointerup);
55054
55055                         event.stopPropagation();
55056                     }
55057                 }
55058
55059
55060                 function pointermove() {
55061                     if (!lasso) {
55062                         lasso = uiLasso(context);
55063                         context.surface().call(lasso);
55064                     }
55065
55066                     lasso.p(context.map().mouse());
55067                 }
55068
55069
55070                 function normalize(a, b) {
55071                     return [
55072                         [Math.min(a[0], b[0]), Math.min(a[1], b[1])],
55073                         [Math.max(a[0], b[0]), Math.max(a[1], b[1])]
55074                     ];
55075                 }
55076
55077
55078                 function lassoed() {
55079                     if (!lasso) return [];
55080
55081                     var graph = context.graph();
55082                     var limitToNodes;
55083
55084                     if (context.map().editableDataEnabled(true /* skipZoomCheck */) && context.map().isInWideSelection()) {
55085                         // only select from the visible nodes
55086                         limitToNodes = new Set(utilGetAllNodes(context.selectedIDs(), graph));
55087                     } else if (!context.map().editableDataEnabled()) {
55088                         return [];
55089                     }
55090
55091                     var bounds = lasso.extent().map(context.projection.invert);
55092                     var extent = geoExtent(normalize(bounds[0], bounds[1]));
55093
55094                     var intersects = context.history().intersects(extent).filter(function(entity) {
55095                         return entity.type === 'node' &&
55096                             (!limitToNodes || limitToNodes.has(entity)) &&
55097                             geoPointInPolygon(context.projection(entity.loc), lasso.coordinates) &&
55098                             !context.features().isHidden(entity, graph, entity.geometry(graph));
55099                     });
55100
55101                     // sort the lassoed nodes as best we can
55102                     intersects.sort(function(node1, node2) {
55103                         var parents1 = graph.parentWays(node1);
55104                         var parents2 = graph.parentWays(node2);
55105                         if (parents1.length && parents2.length) {
55106                             // both nodes are vertices
55107
55108                             var sharedParents = utilArrayIntersection(parents1, parents2);
55109                             if (sharedParents.length) {
55110                                 var sharedParentNodes = sharedParents[0].nodes;
55111                                 // vertices are members of the same way; sort them in their listed order
55112                                 return sharedParentNodes.indexOf(node1.id) -
55113                                     sharedParentNodes.indexOf(node2.id);
55114                             } else {
55115                                 // vertices do not share a way; group them by their respective parent ways
55116                                 return parseFloat(parents1[0].id.slice(1)) -
55117                                     parseFloat(parents2[0].id.slice(1));
55118                             }
55119
55120                         } else if (parents1.length || parents2.length) {
55121                             // only one node is a vertex; sort standalone points before vertices
55122                             return parents1.length - parents2.length;
55123                         }
55124                         // both nodes are standalone points; sort left to right
55125                         return node1.loc[0] - node2.loc[0];
55126                     });
55127
55128                     return intersects.map(function(entity) { return entity.id; });
55129                 }
55130
55131
55132                 function pointerup() {
55133                     select(window)
55134                         .on(_pointerPrefix + 'move.lasso', null)
55135                         .on(_pointerPrefix + 'up.lasso', null);
55136
55137                     if (!lasso) return;
55138
55139                     var ids = lassoed();
55140                     lasso.close();
55141
55142                     if (ids.length) {
55143                         context.enter(modeSelect(context, ids));
55144                     }
55145                 }
55146
55147                 selection
55148                     .on(_pointerPrefix + 'down.lasso', pointerdown);
55149             };
55150
55151
55152             behavior.off = function(selection) {
55153                 selection.on(_pointerPrefix + 'down.lasso', null);
55154             };
55155
55156
55157             return behavior;
55158         }
55159
55160         function modeBrowse(context) {
55161             var mode = {
55162                 button: 'browse',
55163                 id: 'browse',
55164                 title: _t('modes.browse.title'),
55165                 description: _t('modes.browse.description')
55166             };
55167             var sidebar;
55168
55169             var _selectBehavior;
55170             var _behaviors = [];
55171
55172
55173             mode.selectBehavior = function(val) {
55174                 if (!arguments.length) return _selectBehavior;
55175                 _selectBehavior = val;
55176                 return mode;
55177             };
55178
55179
55180             mode.enter = function() {
55181                 if (!_behaviors.length) {
55182                     if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
55183                     _behaviors = [
55184                         behaviorPaste(context),
55185                         behaviorHover(context).on('hover', context.ui().sidebar.hover),
55186                         _selectBehavior,
55187                         behaviorLasso(context),
55188                         modeDragNode(context).behavior,
55189                         modeDragNote(context).behavior
55190                     ];
55191                 }
55192                 _behaviors.forEach(context.install);
55193
55194                 // Get focus on the body.
55195                 if (document.activeElement && document.activeElement.blur) {
55196                     document.activeElement.blur();
55197                 }
55198
55199                 if (sidebar) {
55200                     context.ui().sidebar.show(sidebar);
55201                 } else {
55202                     context.ui().sidebar.select(null);
55203                 }
55204             };
55205
55206
55207             mode.exit = function() {
55208                 context.ui().sidebar.hover.cancel();
55209                 _behaviors.forEach(context.uninstall);
55210
55211                 if (sidebar) {
55212                     context.ui().sidebar.hide();
55213                 }
55214             };
55215
55216
55217             mode.sidebar = function(_) {
55218                 if (!arguments.length) return sidebar;
55219                 sidebar = _;
55220                 return mode;
55221             };
55222
55223
55224             mode.operations = function() {
55225                 return [operationPaste(context)];
55226             };
55227
55228
55229             return mode;
55230         }
55231
55232         function behaviorAddWay(context) {
55233             var dispatch$1 = dispatch('start', 'startFromWay', 'startFromNode');
55234             var draw = behaviorDraw(context);
55235
55236             function behavior(surface) {
55237                 draw.on('click', function() { dispatch$1.apply('start', this, arguments); })
55238                     .on('clickWay', function() { dispatch$1.apply('startFromWay', this, arguments); })
55239                     .on('clickNode', function() { dispatch$1.apply('startFromNode', this, arguments); })
55240                     .on('cancel', behavior.cancel)
55241                     .on('finish', behavior.cancel);
55242
55243                 context.map()
55244                     .dblclickZoomEnable(false);
55245
55246                 surface.call(draw);
55247             }
55248
55249
55250             behavior.off = function(surface) {
55251                 surface.call(draw.off);
55252             };
55253
55254
55255             behavior.cancel = function() {
55256                 window.setTimeout(function() {
55257                     context.map().dblclickZoomEnable(true);
55258                 }, 1000);
55259
55260                 context.enter(modeBrowse(context));
55261             };
55262
55263
55264             return utilRebind(behavior, dispatch$1, 'on');
55265         }
55266
55267         function behaviorHash(context) {
55268
55269             // cached window.location.hash
55270             var _cachedHash = null;
55271             // allowable latitude range
55272             var _latitudeLimit = 90 - 1e-8;
55273
55274             function computedHashParameters() {
55275                 var map = context.map();
55276                 var center = map.center();
55277                 var zoom = map.zoom();
55278                 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
55279                 var oldParams = utilObjectOmit(utilStringQs(window.location.hash),
55280                     ['comment', 'source', 'hashtags', 'walkthrough']
55281                 );
55282                 var newParams = {};
55283
55284                 delete oldParams.id;
55285                 var selected = context.selectedIDs().filter(function(id) {
55286                     return context.hasEntity(id);
55287                 });
55288                 if (selected.length) {
55289                     newParams.id = selected.join(',');
55290                 }
55291
55292                 newParams.map = zoom.toFixed(2) +
55293                     '/' + center[1].toFixed(precision) +
55294                     '/' + center[0].toFixed(precision);
55295
55296                 return Object.assign(oldParams, newParams);
55297             }
55298
55299             function computedHash() {
55300                 return '#' + utilQsString(computedHashParameters(), true);
55301             }
55302
55303             function computedTitle(includeChangeCount) {
55304
55305                 var baseTitle = context.documentTitleBase() || 'iD';
55306                 var contextual;
55307                 var changeCount;
55308                 var titleID;
55309
55310                 var selected = context.selectedIDs().filter(function(id) {
55311                     return context.hasEntity(id);
55312                 });
55313                 if (selected.length) {
55314                     var firstLabel = utilDisplayLabel(context.entity(selected[0]), context.graph());
55315                     if (selected.length > 1 ) {
55316                         contextual = _t('title.labeled_and_more', {
55317                             labeled: firstLabel,
55318                             count: (selected.length - 1).toString()
55319                         });
55320                     } else {
55321                         contextual = firstLabel;
55322                     }
55323                     titleID = 'context';
55324                 }
55325
55326                 if (includeChangeCount) {
55327                     changeCount = context.history().difference().summary().length;
55328                     if (changeCount > 0) {
55329                         titleID = contextual ? 'changes_context' : 'changes';
55330                     }
55331                 }
55332
55333                 if (titleID) {
55334                     return _t('title.format.' + titleID, {
55335                         changes: changeCount,
55336                         base: baseTitle,
55337                         context: contextual
55338                     });
55339                 }
55340
55341                 return baseTitle;
55342             }
55343
55344             function updateTitle(includeChangeCount) {
55345                 if (!context.setsDocumentTitle()) return;
55346
55347                 var newTitle = computedTitle(includeChangeCount);
55348                 if (document.title !== newTitle) {
55349                     document.title = newTitle;
55350                 }
55351             }
55352
55353             function updateHashIfNeeded() {
55354                 if (context.inIntro()) return;
55355
55356                 var latestHash = computedHash();
55357                 if (_cachedHash !== latestHash) {
55358                     _cachedHash = latestHash;
55359
55360                     // Update the URL hash without affecting the browser navigation stack,
55361                     // though unavoidably creating a browser history entry
55362                     window.history.replaceState(null, computedTitle(false /* includeChangeCount */), latestHash);
55363
55364                     // set the title we want displayed for the browser tab/window
55365                     updateTitle(true /* includeChangeCount */);
55366                 }
55367             }
55368
55369             var _throttledUpdate = throttle(updateHashIfNeeded, 500);
55370             var _throttledUpdateTitle = throttle(function() {
55371                 updateTitle(true /* includeChangeCount */);
55372             }, 500);
55373
55374             function hashchange() {
55375
55376                 // ignore spurious hashchange events
55377                 if (window.location.hash === _cachedHash) return;
55378
55379                 _cachedHash = window.location.hash;
55380
55381                 var q = utilStringQs(_cachedHash);
55382                 var mapArgs = (q.map || '').split('/').map(Number);
55383
55384                 if (mapArgs.length < 3 || mapArgs.some(isNaN)) {
55385                     // replace bogus hash
55386                     updateHashIfNeeded();
55387
55388                 } else {
55389                     // don't update if the new hash already reflects the state of iD
55390                     if (_cachedHash === computedHash()) return;
55391
55392                     var mode = context.mode();
55393
55394                     context.map().centerZoom([mapArgs[2], Math.min(_latitudeLimit, Math.max(-_latitudeLimit, mapArgs[1]))], mapArgs[0]);
55395
55396                     if (q.id) {
55397                         var ids = q.id.split(',').filter(function(id) {
55398                             return context.hasEntity(id);
55399                         });
55400                         var skip = mode && mode.id === 'select' && utilArrayIdentical(mode.selectedIDs(), ids);
55401                         if (ids.length && !skip) {
55402                             context.enter(modeSelect(context, ids));
55403                             return;
55404                         }
55405                     }
55406
55407                     var center = context.map().center();
55408                     var dist = geoSphericalDistance(center, [mapArgs[2], mapArgs[1]]);
55409                     var maxdist = 500;
55410
55411                     // Don't allow the hash location to change too much while drawing
55412                     // This can happen if the user accidently hit the back button.  #3996
55413                     if (mode && mode.id.match(/^draw/) !== null && dist > maxdist) {
55414                         context.enter(modeBrowse(context));
55415                         return;
55416                     }
55417                 }
55418             }
55419
55420             function behavior() {
55421                 context.map()
55422                     .on('move.behaviorHash', _throttledUpdate);
55423
55424                 context.history()
55425                     .on('change.behaviorHash', _throttledUpdateTitle);
55426
55427                 context
55428                     .on('enter.behaviorHash', _throttledUpdate);
55429
55430                 select(window)
55431                     .on('hashchange.behaviorHash', hashchange);
55432
55433                 if (window.location.hash) {
55434                     var q = utilStringQs(window.location.hash);
55435
55436                     if (q.id) {
55437                         //if (!context.history().hasRestorableChanges()) {
55438                             // targeting specific features: download, select, and zoom to them
55439                             context.zoomToEntity(q.id.split(',')[0], !q.map);
55440                         //}
55441                     }
55442
55443                     if (q.walkthrough === 'true') {
55444                         behavior.startWalkthrough = true;
55445                     }
55446
55447                     if (q.map) {
55448                         behavior.hadHash = true;
55449                     }
55450
55451                     hashchange();
55452
55453                     updateTitle(false);
55454                 }
55455             }
55456
55457             behavior.off = function() {
55458                 _throttledUpdate.cancel();
55459                 _throttledUpdateTitle.cancel();
55460
55461                 context.map()
55462                     .on('move.behaviorHash', null);
55463
55464                 context
55465                     .on('enter.behaviorHash', null);
55466
55467                 select(window)
55468                     .on('hashchange.behaviorHash', null);
55469
55470                 window.location.hash = '';
55471             };
55472
55473             return behavior;
55474         }
55475
55476         /*
55477             iD.coreDifference represents the difference between two graphs.
55478             It knows how to calculate the set of entities that were
55479             created, modified, or deleted, and also contains the logic
55480             for recursively extending a difference to the complete set
55481             of entities that will require a redraw, taking into account
55482             child and parent relationships.
55483          */
55484         function coreDifference(base, head) {
55485             var _changes = {};
55486             var _didChange = {};  // 'addition', 'deletion', 'geometry', 'properties'
55487             var _diff = {};
55488
55489             function checkEntityID(id) {
55490                 var h = head.entities[id];
55491                 var b = base.entities[id];
55492
55493                 if (h === b) return;
55494                 if (_changes[id]) return;
55495
55496                 if (!h && b) {
55497                     _changes[id] = { base: b, head: h };
55498                     _didChange.deletion = true;
55499                     return;
55500                 }
55501                 if (h && !b) {
55502                     _changes[id] = { base: b, head: h };
55503                     _didChange.addition = true;
55504                     return;
55505                 }
55506
55507                 if (h && b) {
55508                     if (h.members && b.members && !fastDeepEqual(h.members, b.members)) {
55509                         _changes[id] = { base: b, head: h };
55510                         _didChange.geometry = true;
55511                         _didChange.properties = true;
55512                         return;
55513                     }
55514                     if (h.loc && b.loc && !geoVecEqual(h.loc, b.loc)) {
55515                         _changes[id] = { base: b, head: h };
55516                         _didChange.geometry = true;
55517                     }
55518                     if (h.nodes && b.nodes && !fastDeepEqual(h.nodes, b.nodes)) {
55519                         _changes[id] = { base: b, head: h };
55520                         _didChange.geometry = true;
55521                     }
55522                     if (h.tags && b.tags && !fastDeepEqual(h.tags, b.tags)) {
55523                         _changes[id] = { base: b, head: h };
55524                         _didChange.properties = true;
55525                     }
55526                 }
55527             }
55528
55529             function load() {
55530                 // HOT CODE: there can be many thousands of downloaded entities, so looping
55531                 // through them all can become a performance bottleneck. Optimize by
55532                 // resolving duplicates and using a basic `for` loop
55533                 var ids = utilArrayUniq(Object.keys(head.entities).concat(Object.keys(base.entities)));
55534                 for (var i = 0; i < ids.length; i++) {
55535                     checkEntityID(ids[i]);
55536                 }
55537             }
55538             load();
55539
55540
55541             _diff.length = function length() {
55542                 return Object.keys(_changes).length;
55543             };
55544
55545
55546             _diff.changes = function changes() {
55547                 return _changes;
55548             };
55549
55550             _diff.didChange = _didChange;
55551
55552
55553             // pass true to include affected relation members
55554             _diff.extantIDs = function extantIDs(includeRelMembers) {
55555                 var result = new Set();
55556                 Object.keys(_changes).forEach(function(id) {
55557                     if (_changes[id].head) {
55558                         result.add(id);
55559                     }
55560
55561                     var h = _changes[id].head;
55562                     var b = _changes[id].base;
55563                     var entity = h || b;
55564
55565                     if (includeRelMembers && entity.type === 'relation') {
55566                         var mh = h ? h.members.map(function(m) { return m.id; }) : [];
55567                         var mb = b ? b.members.map(function(m) { return m.id; }) : [];
55568                         utilArrayUnion(mh, mb).forEach(function(memberID) {
55569                             if (head.hasEntity(memberID)) {
55570                                 result.add(memberID);
55571                             }
55572                         });
55573                     }
55574                 });
55575
55576                 return Array.from(result);
55577             };
55578
55579
55580             _diff.modified = function modified() {
55581                 var result = [];
55582                 Object.values(_changes).forEach(function(change) {
55583                     if (change.base && change.head) {
55584                         result.push(change.head);
55585                     }
55586                 });
55587                 return result;
55588             };
55589
55590
55591             _diff.created = function created() {
55592                 var result = [];
55593                 Object.values(_changes).forEach(function(change) {
55594                     if (!change.base && change.head) {
55595                         result.push(change.head);
55596                     }
55597                 });
55598                 return result;
55599             };
55600
55601
55602             _diff.deleted = function deleted() {
55603                 var result = [];
55604                 Object.values(_changes).forEach(function(change) {
55605                     if (change.base && !change.head) {
55606                         result.push(change.base);
55607                     }
55608                 });
55609                 return result;
55610             };
55611
55612
55613             _diff.summary = function summary() {
55614                 var relevant = {};
55615
55616                 var keys = Object.keys(_changes);
55617                 for (var i = 0; i < keys.length; i++) {
55618                     var change = _changes[keys[i]];
55619
55620                     if (change.head && change.head.geometry(head) !== 'vertex') {
55621                         addEntity(change.head, head, change.base ? 'modified' : 'created');
55622
55623                     } else if (change.base && change.base.geometry(base) !== 'vertex') {
55624                         addEntity(change.base, base, 'deleted');
55625
55626                     } else if (change.base && change.head) { // modified vertex
55627                         var moved    = !fastDeepEqual(change.base.loc,  change.head.loc);
55628                         var retagged = !fastDeepEqual(change.base.tags, change.head.tags);
55629
55630                         if (moved) {
55631                             addParents(change.head);
55632                         }
55633
55634                         if (retagged || (moved && change.head.hasInterestingTags())) {
55635                             addEntity(change.head, head, 'modified');
55636                         }
55637
55638                     } else if (change.head && change.head.hasInterestingTags()) { // created vertex
55639                         addEntity(change.head, head, 'created');
55640
55641                     } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex
55642                         addEntity(change.base, base, 'deleted');
55643                     }
55644                 }
55645
55646                 return Object.values(relevant);
55647
55648
55649                 function addEntity(entity, graph, changeType) {
55650                     relevant[entity.id] = {
55651                         entity: entity,
55652                         graph: graph,
55653                         changeType: changeType
55654                     };
55655                 }
55656
55657                 function addParents(entity) {
55658                     var parents = head.parentWays(entity);
55659                     for (var j = parents.length - 1; j >= 0; j--) {
55660                         var parent = parents[j];
55661                         if (!(parent.id in relevant)) {
55662                             addEntity(parent, head, 'modified');
55663                         }
55664                     }
55665                 }
55666             };
55667
55668
55669             // returns complete set of entities that require a redraw
55670             //  (optionally within given `extent`)
55671             _diff.complete = function complete(extent) {
55672                 var result = {};
55673                 var id, change;
55674
55675                 for (id in _changes) {
55676                     change = _changes[id];
55677
55678                     var h = change.head;
55679                     var b = change.base;
55680                     var entity = h || b;
55681                     var i;
55682
55683                     if (extent &&
55684                         (!h || !h.intersects(extent, head)) &&
55685                         (!b || !b.intersects(extent, base)))
55686                         continue;
55687
55688                     result[id] = h;
55689
55690                     if (entity.type === 'way') {
55691                         var nh = h ? h.nodes : [];
55692                         var nb = b ? b.nodes : [];
55693                         var diff;
55694
55695                         diff = utilArrayDifference(nh, nb);
55696                         for (i = 0; i < diff.length; i++) {
55697                             result[diff[i]] = head.hasEntity(diff[i]);
55698                         }
55699
55700                         diff = utilArrayDifference(nb, nh);
55701                         for (i = 0; i < diff.length; i++) {
55702                             result[diff[i]] = head.hasEntity(diff[i]);
55703                         }
55704                     }
55705
55706                     if (entity.type === 'relation' && entity.isMultipolygon()) {
55707                         var mh = h ? h.members.map(function(m) { return m.id; }) : [];
55708                         var mb = b ? b.members.map(function(m) { return m.id; }) : [];
55709                         var ids = utilArrayUnion(mh, mb);
55710                         for (i = 0; i < ids.length; i++) {
55711                             var member = head.hasEntity(ids[i]);
55712                             if (!member) continue;   // not downloaded
55713                             if (extent && !member.intersects(extent, head)) continue;   // not visible
55714                             result[ids[i]] = member;
55715                         }
55716                     }
55717
55718                     addParents(head.parentWays(entity), result);
55719                     addParents(head.parentRelations(entity), result);
55720                 }
55721
55722                 return result;
55723
55724
55725                 function addParents(parents, result) {
55726                     for (var i = 0; i < parents.length; i++) {
55727                         var parent = parents[i];
55728                         if (parent.id in result) continue;
55729
55730                         result[parent.id] = parent;
55731                         addParents(head.parentRelations(parent), result);
55732                     }
55733                 }
55734             };
55735
55736
55737             return _diff;
55738         }
55739
55740         function coreTree(head) {
55741             // tree for entities
55742             var _rtree = new RBush();
55743             var _bboxes = {};
55744
55745             // maintain a separate tree for granular way segments
55746             var _segmentsRTree = new RBush();
55747             var _segmentsBBoxes = {};
55748             var _segmentsByWayId = {};
55749
55750             var tree = {};
55751
55752
55753             function entityBBox(entity) {
55754                 var bbox = entity.extent(head).bbox();
55755                 bbox.id = entity.id;
55756                 _bboxes[entity.id] = bbox;
55757                 return bbox;
55758             }
55759
55760
55761             function segmentBBox(segment) {
55762                 var extent = segment.extent(head);
55763                 // extent can be null if the node entites aren't in the graph for some reason
55764                 if (!extent) return null;
55765
55766                 var bbox = extent.bbox();
55767                 bbox.segment = segment;
55768                 _segmentsBBoxes[segment.id] = bbox;
55769                 return bbox;
55770             }
55771
55772
55773             function removeEntity(entity) {
55774                 _rtree.remove(_bboxes[entity.id]);
55775                 delete _bboxes[entity.id];
55776
55777                 if (_segmentsByWayId[entity.id]) {
55778                     _segmentsByWayId[entity.id].forEach(function(segment) {
55779                         _segmentsRTree.remove(_segmentsBBoxes[segment.id]);
55780                         delete _segmentsBBoxes[segment.id];
55781                     });
55782                     delete _segmentsByWayId[entity.id];
55783                 }
55784             }
55785
55786
55787             function loadEntities(entities) {
55788                 _rtree.load(entities.map(entityBBox));
55789
55790                 var segments = [];
55791                 entities.forEach(function(entity) {
55792                     if (entity.segments) {
55793                         var entitySegments = entity.segments(head);
55794                         // cache these to make them easy to remove later
55795                         _segmentsByWayId[entity.id] = entitySegments;
55796                         segments = segments.concat(entitySegments);
55797                     }
55798                 });
55799                 if (segments.length) _segmentsRTree.load(segments.map(segmentBBox).filter(Boolean));
55800             }
55801
55802
55803             function updateParents(entity, insertions, memo) {
55804                 head.parentWays(entity).forEach(function(way) {
55805                     if (_bboxes[way.id]) {
55806                         removeEntity(way);
55807                         insertions[way.id] = way;
55808                     }
55809                     updateParents(way, insertions, memo);
55810                 });
55811
55812                 head.parentRelations(entity).forEach(function(relation) {
55813                     if (memo[entity.id]) return;
55814                     memo[entity.id] = true;
55815                     if (_bboxes[relation.id]) {
55816                         removeEntity(relation);
55817                         insertions[relation.id] = relation;
55818                     }
55819                     updateParents(relation, insertions, memo);
55820                 });
55821             }
55822
55823
55824             tree.rebase = function(entities, force) {
55825                 var insertions = {};
55826
55827                 for (var i = 0; i < entities.length; i++) {
55828                     var entity = entities[i];
55829                     if (!entity.visible) continue;
55830
55831                     if (head.entities.hasOwnProperty(entity.id) || _bboxes[entity.id]) {
55832                         if (!force) {
55833                             continue;
55834                         } else if (_bboxes[entity.id]) {
55835                             removeEntity(entity);
55836                         }
55837                     }
55838
55839                     insertions[entity.id] = entity;
55840                     updateParents(entity, insertions, {});
55841                 }
55842
55843                 loadEntities(Object.values(insertions));
55844
55845                 return tree;
55846             };
55847
55848
55849             function updateToGraph(graph) {
55850                 if (graph === head) return;
55851
55852                 var diff = coreDifference(head, graph);
55853
55854                 head = graph;
55855
55856                 var changed = diff.didChange;
55857                 if (!changed.addition && !changed.deletion && !changed.geometry) return;
55858
55859                 var insertions = {};
55860
55861                 if (changed.deletion) {
55862                     diff.deleted().forEach(function(entity) {
55863                         removeEntity(entity);
55864                     });
55865                 }
55866
55867                 if (changed.geometry) {
55868                     diff.modified().forEach(function(entity) {
55869                         removeEntity(entity);
55870                         insertions[entity.id] = entity;
55871                         updateParents(entity, insertions, {});
55872                     });
55873                 }
55874
55875                 if (changed.addition) {
55876                     diff.created().forEach(function(entity) {
55877                         insertions[entity.id] = entity;
55878                     });
55879                 }
55880
55881                 loadEntities(Object.values(insertions));
55882             }
55883
55884             // returns an array of entities with bounding boxes overlapping `extent` for the given `graph`
55885             tree.intersects = function(extent, graph) {
55886                 updateToGraph(graph);
55887                 return _rtree.search(extent.bbox())
55888                     .map(function(bbox) { return graph.entity(bbox.id); });
55889             };
55890
55891             // returns an array of segment objects with bounding boxes overlapping `extent` for the given `graph`
55892             tree.waySegments = function(extent, graph) {
55893                 updateToGraph(graph);
55894                 return _segmentsRTree.search(extent.bbox())
55895                     .map(function(bbox) { return bbox.segment; });
55896             };
55897
55898
55899             return tree;
55900         }
55901
55902         function uiModal(selection, blocking) {
55903           let keybinding = utilKeybinding('modal');
55904           let previous = selection.select('div.modal');
55905           let animate = previous.empty();
55906
55907           previous.transition()
55908             .duration(200)
55909             .style('opacity', 0)
55910             .remove();
55911
55912           let shaded = selection
55913             .append('div')
55914             .attr('class', 'shaded')
55915             .style('opacity', 0);
55916
55917           shaded.close = () => {
55918             shaded
55919               .transition()
55920               .duration(200)
55921               .style('opacity',0)
55922               .remove();
55923
55924             modal
55925               .transition()
55926               .duration(200)
55927               .style('top','0px');
55928
55929             select(document)
55930               .call(keybinding.unbind);
55931           };
55932
55933
55934           let modal = shaded
55935             .append('div')
55936             .attr('class', 'modal fillL');
55937
55938           if (!blocking) {
55939             shaded.on('click.remove-modal', () => {
55940               if (event.target === this) {
55941                 shaded.close();
55942               }
55943             });
55944
55945             modal
55946               .append('button')
55947               .attr('class', 'close')
55948               .on('click', shaded.close)
55949               .call(svgIcon('#iD-icon-close'));
55950
55951             keybinding
55952               .on('⌫', shaded.close)
55953               .on('⎋', shaded.close);
55954
55955             select(document)
55956               .call(keybinding);
55957           }
55958
55959           modal
55960             .append('div')
55961             .attr('class', 'content');
55962
55963           if (animate) {
55964             shaded.transition().style('opacity', 1);
55965           } else {
55966             shaded.style('opacity', 1);
55967           }
55968
55969           return shaded;
55970         }
55971
55972         function uiLoading(context) {
55973           let _modalSelection = select(null);
55974           let _message = '';
55975           let _blocking = false;
55976
55977
55978           let loading = (selection) => {
55979             _modalSelection = uiModal(selection, _blocking);
55980
55981             let loadertext = _modalSelection.select('.content')
55982               .classed('loading-modal', true)
55983               .append('div')
55984               .attr('class', 'modal-section fillL');
55985
55986             loadertext
55987               .append('img')
55988               .attr('class', 'loader')
55989               .attr('src', context.imagePath('loader-white.gif'));
55990
55991             loadertext
55992               .append('h3')
55993               .text(_message);
55994
55995             _modalSelection.select('button.close')
55996               .attr('class', 'hide');
55997
55998             return loading;
55999           };
56000
56001
56002           loading.message = function(val) {
56003             if (!arguments.length) return _message;
56004             _message = val;
56005             return loading;
56006           };
56007
56008
56009           loading.blocking = function(val) {
56010             if (!arguments.length) return _blocking;
56011             _blocking = val;
56012             return loading;
56013           };
56014
56015
56016           loading.close = () => {
56017             _modalSelection.remove();
56018           };
56019
56020
56021           loading.isShown = () => {
56022             return _modalSelection && !_modalSelection.empty() && _modalSelection.node().parentNode;
56023           };
56024
56025
56026           return loading;
56027         }
56028
56029         function coreHistory(context) {
56030             var dispatch$1 = dispatch('change', 'merge', 'restore', 'undone', 'redone');
56031             var lock = utilSessionMutex('lock');
56032
56033             // restorable if iD not open in another window/tab and a saved history exists in localStorage
56034             var _hasUnresolvedRestorableChanges = lock.lock() && !!corePreferences(getKey('saved_history'));
56035
56036             var duration = 150;
56037             var _imageryUsed = [];
56038             var _photoOverlaysUsed = [];
56039             var _checkpoints = {};
56040             var _pausedGraph;
56041             var _stack;
56042             var _index;
56043             var _tree;
56044
56045
56046             // internal _act, accepts list of actions and eased time
56047             function _act(actions, t) {
56048                 actions = Array.prototype.slice.call(actions);
56049
56050                 var annotation;
56051                 if (typeof actions[actions.length - 1] !== 'function') {
56052                     annotation = actions.pop();
56053                 }
56054
56055                 var graph = _stack[_index].graph;
56056                 for (var i = 0; i < actions.length; i++) {
56057                     graph = actions[i](graph, t);
56058                 }
56059
56060                 return {
56061                     graph: graph,
56062                     annotation: annotation,
56063                     imageryUsed: _imageryUsed,
56064                     photoOverlaysUsed: _photoOverlaysUsed,
56065                     transform: context.projection.transform(),
56066                     selectedIDs: context.selectedIDs()
56067                 };
56068             }
56069
56070
56071             // internal _perform with eased time
56072             function _perform(args, t) {
56073                 var previous = _stack[_index].graph;
56074                 _stack = _stack.slice(0, _index + 1);
56075                 var actionResult = _act(args, t);
56076                 _stack.push(actionResult);
56077                 _index++;
56078                 return change(previous);
56079             }
56080
56081
56082             // internal _replace with eased time
56083             function _replace(args, t) {
56084                 var previous = _stack[_index].graph;
56085                 // assert(_index == _stack.length - 1)
56086                 var actionResult = _act(args, t);
56087                 _stack[_index] = actionResult;
56088                 return change(previous);
56089             }
56090
56091
56092             // internal _overwrite with eased time
56093             function _overwrite(args, t) {
56094                 var previous = _stack[_index].graph;
56095                 if (_index > 0) {
56096                     _index--;
56097                     _stack.pop();
56098                 }
56099                 _stack = _stack.slice(0, _index + 1);
56100                 var actionResult = _act(args, t);
56101                 _stack.push(actionResult);
56102                 _index++;
56103                 return change(previous);
56104             }
56105
56106
56107             // determine difference and dispatch a change event
56108             function change(previous) {
56109                 var difference = coreDifference(previous, history.graph());
56110                 if (!_pausedGraph) {
56111                     dispatch$1.call('change', this, difference);
56112                 }
56113                 return difference;
56114             }
56115
56116
56117             // iD uses namespaced keys so multiple installations do not conflict
56118             function getKey(n) {
56119                 return 'iD_' + window.location.origin + '_' + n;
56120             }
56121
56122
56123             var history = {
56124
56125                 graph: function() {
56126                     return _stack[_index].graph;
56127                 },
56128
56129
56130                 tree: function() {
56131                     return _tree;
56132                 },
56133
56134
56135                 base: function() {
56136                     return _stack[0].graph;
56137                 },
56138
56139
56140                 merge: function(entities/*, extent*/) {
56141                     var stack = _stack.map(function(state) { return state.graph; });
56142                     _stack[0].graph.rebase(entities, stack, false);
56143                     _tree.rebase(entities, false);
56144
56145                     dispatch$1.call('merge', this, entities);
56146                 },
56147
56148
56149                 perform: function() {
56150                     // complete any transition already in progress
56151                     select(document).interrupt('history.perform');
56152
56153                     var transitionable = false;
56154                     var action0 = arguments[0];
56155
56156                     if (arguments.length === 1 ||
56157                         (arguments.length === 2 && (typeof arguments[1] !== 'function'))) {
56158                         transitionable = !!action0.transitionable;
56159                     }
56160
56161                     if (transitionable) {
56162                         var origArguments = arguments;
56163                         select(document)
56164                             .transition('history.perform')
56165                             .duration(duration)
56166                             .ease(linear$1)
56167                             .tween('history.tween', function() {
56168                                 return function(t) {
56169                                     if (t < 1) _overwrite([action0], t);
56170                                 };
56171                             })
56172                             .on('start', function() {
56173                                 _perform([action0], 0);
56174                             })
56175                             .on('end interrupt', function() {
56176                                 _overwrite(origArguments, 1);
56177                             });
56178
56179                     } else {
56180                         return _perform(arguments);
56181                     }
56182                 },
56183
56184
56185                 replace: function() {
56186                     select(document).interrupt('history.perform');
56187                     return _replace(arguments, 1);
56188                 },
56189
56190
56191                 // Same as calling pop and then perform
56192                 overwrite: function() {
56193                     select(document).interrupt('history.perform');
56194                     return _overwrite(arguments, 1);
56195                 },
56196
56197
56198                 pop: function(n) {
56199                     select(document).interrupt('history.perform');
56200
56201                     var previous = _stack[_index].graph;
56202                     if (isNaN(+n) || +n < 0) {
56203                         n = 1;
56204                     }
56205                     while (n-- > 0 && _index > 0) {
56206                         _index--;
56207                         _stack.pop();
56208                     }
56209                     return change(previous);
56210                 },
56211
56212
56213                 // Back to the previous annotated state or _index = 0.
56214                 undo: function() {
56215                     select(document).interrupt('history.perform');
56216
56217                     var previousStack = _stack[_index];
56218                     var previous = previousStack.graph;
56219                     while (_index > 0) {
56220                         _index--;
56221                         if (_stack[_index].annotation) break;
56222                     }
56223
56224                     dispatch$1.call('undone', this, _stack[_index], previousStack);
56225                     return change(previous);
56226                 },
56227
56228
56229                 // Forward to the next annotated state.
56230                 redo: function() {
56231                     select(document).interrupt('history.perform');
56232
56233                     var previousStack = _stack[_index];
56234                     var previous = previousStack.graph;
56235                     var tryIndex = _index;
56236                     while (tryIndex < _stack.length - 1) {
56237                         tryIndex++;
56238                         if (_stack[tryIndex].annotation) {
56239                             _index = tryIndex;
56240                             dispatch$1.call('redone', this, _stack[_index], previousStack);
56241                             break;
56242                         }
56243                     }
56244
56245                     return change(previous);
56246                 },
56247
56248
56249                 pauseChangeDispatch: function() {
56250                     if (!_pausedGraph) {
56251                         _pausedGraph = _stack[_index].graph;
56252                     }
56253                 },
56254
56255
56256                 resumeChangeDispatch: function() {
56257                     if (_pausedGraph) {
56258                         var previous = _pausedGraph;
56259                         _pausedGraph = null;
56260                         return change(previous);
56261                     }
56262                 },
56263
56264
56265                 undoAnnotation: function() {
56266                     var i = _index;
56267                     while (i >= 0) {
56268                         if (_stack[i].annotation) return _stack[i].annotation;
56269                         i--;
56270                     }
56271                 },
56272
56273
56274                 redoAnnotation: function() {
56275                     var i = _index + 1;
56276                     while (i <= _stack.length - 1) {
56277                         if (_stack[i].annotation) return _stack[i].annotation;
56278                         i++;
56279                     }
56280                 },
56281
56282
56283                 // Returns the entities from the active graph with bounding boxes
56284                 // overlapping the given `extent`.
56285                 intersects: function(extent) {
56286                     return _tree.intersects(extent, _stack[_index].graph);
56287                 },
56288
56289
56290                 difference: function() {
56291                     var base = _stack[0].graph;
56292                     var head = _stack[_index].graph;
56293                     return coreDifference(base, head);
56294                 },
56295
56296
56297                 changes: function(action) {
56298                     var base = _stack[0].graph;
56299                     var head = _stack[_index].graph;
56300
56301                     if (action) {
56302                         head = action(head);
56303                     }
56304
56305                     var difference = coreDifference(base, head);
56306
56307                     return {
56308                         modified: difference.modified(),
56309                         created: difference.created(),
56310                         deleted: difference.deleted()
56311                     };
56312                 },
56313
56314
56315                 hasChanges: function() {
56316                     return this.difference().length() > 0;
56317                 },
56318
56319
56320                 imageryUsed: function(sources) {
56321                     if (sources) {
56322                         _imageryUsed = sources;
56323                         return history;
56324                     } else {
56325                         var s = new Set();
56326                         _stack.slice(1, _index + 1).forEach(function(state) {
56327                             state.imageryUsed.forEach(function(source) {
56328                                 if (source !== 'Custom') {
56329                                     s.add(source);
56330                                 }
56331                             });
56332                         });
56333                         return Array.from(s);
56334                     }
56335                 },
56336
56337
56338                 photoOverlaysUsed: function(sources) {
56339                     if (sources) {
56340                         _photoOverlaysUsed = sources;
56341                         return history;
56342                     } else {
56343                         var s = new Set();
56344                         _stack.slice(1, _index + 1).forEach(function(state) {
56345                             if (state.photoOverlaysUsed && Array.isArray(state.photoOverlaysUsed)) {
56346                                 state.photoOverlaysUsed.forEach(function(photoOverlay) {
56347                                     s.add(photoOverlay);
56348                                 });
56349                             }
56350                         });
56351                         return Array.from(s);
56352                     }
56353                 },
56354
56355
56356                 // save the current history state
56357                 checkpoint: function(key) {
56358                     _checkpoints[key] = {
56359                         stack: _stack,
56360                         index: _index
56361                     };
56362                     return history;
56363                 },
56364
56365
56366                 // restore history state to a given checkpoint or reset completely
56367                 reset: function(key) {
56368                     if (key !== undefined && _checkpoints.hasOwnProperty(key)) {
56369                         _stack = _checkpoints[key].stack;
56370                         _index = _checkpoints[key].index;
56371                     } else {
56372                         _stack = [{graph: coreGraph()}];
56373                         _index = 0;
56374                         _tree = coreTree(_stack[0].graph);
56375                         _checkpoints = {};
56376                     }
56377                     dispatch$1.call('change');
56378                     return history;
56379                 },
56380
56381
56382                 // `toIntroGraph()` is used to export the intro graph used by the walkthrough.
56383                 //
56384                 // To use it:
56385                 //  1. Start the walkthrough.
56386                 //  2. Get to a "free editing" tutorial step
56387                 //  3. Make your edits to the walkthrough map
56388                 //  4. In your browser dev console run:
56389                 //        `id.history().toIntroGraph()`
56390                 //  5. This outputs stringified JSON to the browser console
56391                 //  6. Copy it to `data/intro_graph.json` and prettify it in your code editor
56392                 toIntroGraph: function() {
56393                     var nextID = { n: 0, r: 0, w: 0 };
56394                     var permIDs = {};
56395                     var graph = this.graph();
56396                     var baseEntities = {};
56397
56398                     // clone base entities..
56399                     Object.values(graph.base().entities).forEach(function(entity) {
56400                         var copy = copyIntroEntity(entity);
56401                         baseEntities[copy.id] = copy;
56402                     });
56403
56404                     // replace base entities with head entities..
56405                     Object.keys(graph.entities).forEach(function(id) {
56406                         var entity = graph.entities[id];
56407                         if (entity) {
56408                             var copy = copyIntroEntity(entity);
56409                             baseEntities[copy.id] = copy;
56410                         } else {
56411                             delete baseEntities[id];
56412                         }
56413                     });
56414
56415                     // swap temporary for permanent ids..
56416                     Object.values(baseEntities).forEach(function(entity) {
56417                         if (Array.isArray(entity.nodes)) {
56418                             entity.nodes = entity.nodes.map(function(node) {
56419                                 return permIDs[node] || node;
56420                             });
56421                         }
56422                         if (Array.isArray(entity.members)) {
56423                             entity.members = entity.members.map(function(member) {
56424                                 member.id = permIDs[member.id] || member.id;
56425                                 return member;
56426                             });
56427                         }
56428                     });
56429
56430                     return JSON.stringify({ dataIntroGraph: baseEntities });
56431
56432
56433                     function copyIntroEntity(source) {
56434                         var copy = utilObjectOmit(source, ['type', 'user', 'v', 'version', 'visible']);
56435
56436                         // Note: the copy is no longer an osmEntity, so it might not have `tags`
56437                         if (copy.tags && !Object.keys(copy.tags)) {
56438                             delete copy.tags;
56439                         }
56440
56441                         if (Array.isArray(copy.loc)) {
56442                             copy.loc[0] = +copy.loc[0].toFixed(6);
56443                             copy.loc[1] = +copy.loc[1].toFixed(6);
56444                         }
56445
56446                         var match = source.id.match(/([nrw])-\d*/);  // temporary id
56447                         if (match !== null) {
56448                             var nrw = match[1];
56449                             var permID;
56450                             do { permID = nrw + (++nextID[nrw]); }
56451                             while (baseEntities.hasOwnProperty(permID));
56452
56453                             copy.id = permIDs[source.id] = permID;
56454                         }
56455                         return copy;
56456                     }
56457                 },
56458
56459
56460                 toJSON: function() {
56461                     if (!this.hasChanges()) return;
56462
56463                     var allEntities = {};
56464                     var baseEntities = {};
56465                     var base = _stack[0];
56466
56467                     var s = _stack.map(function(i) {
56468                         var modified = [];
56469                         var deleted = [];
56470
56471                         Object.keys(i.graph.entities).forEach(function(id) {
56472                             var entity = i.graph.entities[id];
56473                             if (entity) {
56474                                 var key = osmEntity.key(entity);
56475                                 allEntities[key] = entity;
56476                                 modified.push(key);
56477                             } else {
56478                                 deleted.push(id);
56479                             }
56480
56481                             // make sure that the originals of changed or deleted entities get merged
56482                             // into the base of the _stack after restoring the data from JSON.
56483                             if (id in base.graph.entities) {
56484                                 baseEntities[id] = base.graph.entities[id];
56485                             }
56486                             if (entity && entity.nodes) {
56487                                 // get originals of pre-existing child nodes
56488                                 entity.nodes.forEach(function(nodeID) {
56489                                     if (nodeID in base.graph.entities) {
56490                                         baseEntities[nodeID] = base.graph.entities[nodeID];
56491                                     }
56492                                 });
56493                             }
56494                             // get originals of parent entities too
56495                             var baseParents = base.graph._parentWays[id];
56496                             if (baseParents) {
56497                                 baseParents.forEach(function(parentID) {
56498                                     if (parentID in base.graph.entities) {
56499                                         baseEntities[parentID] = base.graph.entities[parentID];
56500                                     }
56501                                 });
56502                             }
56503                         });
56504
56505                         var x = {};
56506
56507                         if (modified.length) x.modified = modified;
56508                         if (deleted.length) x.deleted = deleted;
56509                         if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
56510                         if (i.photoOverlaysUsed) x.photoOverlaysUsed = i.photoOverlaysUsed;
56511                         if (i.annotation) x.annotation = i.annotation;
56512                         if (i.transform) x.transform = i.transform;
56513                         if (i.selectedIDs) x.selectedIDs = i.selectedIDs;
56514
56515                         return x;
56516                     });
56517
56518                     return JSON.stringify({
56519                         version: 3,
56520                         entities: Object.values(allEntities),
56521                         baseEntities: Object.values(baseEntities),
56522                         stack: s,
56523                         nextIDs: osmEntity.id.next,
56524                         index: _index,
56525                         // note the time the changes were saved
56526                         timestamp: (new Date()).getTime()
56527                     });
56528                 },
56529
56530
56531                 fromJSON: function(json, loadChildNodes) {
56532                     var h = JSON.parse(json);
56533                     var loadComplete = true;
56534
56535                     osmEntity.id.next = h.nextIDs;
56536                     _index = h.index;
56537
56538                     if (h.version === 2 || h.version === 3) {
56539                         var allEntities = {};
56540
56541                         h.entities.forEach(function(entity) {
56542                             allEntities[osmEntity.key(entity)] = osmEntity(entity);
56543                         });
56544
56545                         if (h.version === 3) {
56546                             // This merges originals for changed entities into the base of
56547                             // the _stack even if the current _stack doesn't have them (for
56548                             // example when iD has been restarted in a different region)
56549                             var baseEntities = h.baseEntities.map(function(d) { return osmEntity(d); });
56550                             var stack = _stack.map(function(state) { return state.graph; });
56551                             _stack[0].graph.rebase(baseEntities, stack, true);
56552                             _tree.rebase(baseEntities, true);
56553
56554                             // When we restore a modified way, we also need to fetch any missing
56555                             // childnodes that would normally have been downloaded with it.. #2142
56556                             if (loadChildNodes) {
56557                                 var osm = context.connection();
56558                                 var baseWays = baseEntities
56559                                     .filter(function(e) { return e.type === 'way'; });
56560                                 var nodeIDs = baseWays
56561                                     .reduce(function(acc, way) { return utilArrayUnion(acc, way.nodes); }, []);
56562                                 var missing = nodeIDs
56563                                     .filter(function(n) { return !_stack[0].graph.hasEntity(n); });
56564
56565                                 if (missing.length && osm) {
56566                                     loadComplete = false;
56567                                     context.map().redrawEnable(false);
56568
56569                                     var loading = uiLoading(context).blocking(true);
56570                                     context.container().call(loading);
56571
56572                                     var childNodesLoaded = function(err, result) {
56573                                         if (!err) {
56574                                             var visibleGroups = utilArrayGroupBy(result.data, 'visible');
56575                                             var visibles = visibleGroups.true || [];      // alive nodes
56576                                             var invisibles = visibleGroups.false || [];   // deleted nodes
56577
56578                                             if (visibles.length) {
56579                                                 var visibleIDs = visibles.map(function(entity) { return entity.id; });
56580                                                 var stack = _stack.map(function(state) { return state.graph; });
56581                                                 missing = utilArrayDifference(missing, visibleIDs);
56582                                                 _stack[0].graph.rebase(visibles, stack, true);
56583                                                 _tree.rebase(visibles, true);
56584                                             }
56585
56586                                             // fetch older versions of nodes that were deleted..
56587                                             invisibles.forEach(function(entity) {
56588                                                 osm.loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
56589                                             });
56590                                         }
56591
56592                                         if (err || !missing.length) {
56593                                             loading.close();
56594                                             context.map().redrawEnable(true);
56595                                             dispatch$1.call('change');
56596                                             dispatch$1.call('restore', this);
56597                                         }
56598                                     };
56599
56600                                     osm.loadMultiple(missing, childNodesLoaded);
56601                                 }
56602                             }
56603                         }
56604
56605                         _stack = h.stack.map(function(d) {
56606                             var entities = {}, entity;
56607
56608                             if (d.modified) {
56609                                 d.modified.forEach(function(key) {
56610                                     entity = allEntities[key];
56611                                     entities[entity.id] = entity;
56612                                 });
56613                             }
56614
56615                             if (d.deleted) {
56616                                 d.deleted.forEach(function(id) {
56617                                     entities[id] = undefined;
56618                                 });
56619                             }
56620
56621                             return {
56622                                 graph: coreGraph(_stack[0].graph).load(entities),
56623                                 annotation: d.annotation,
56624                                 imageryUsed: d.imageryUsed,
56625                                 photoOverlaysUsed: d.photoOverlaysUsed,
56626                                 transform: d.transform,
56627                                 selectedIDs: d.selectedIDs
56628                             };
56629                         });
56630
56631                     } else { // original version
56632                         _stack = h.stack.map(function(d) {
56633                             var entities = {};
56634
56635                             for (var i in d.entities) {
56636                                 var entity = d.entities[i];
56637                                 entities[i] = entity === 'undefined' ? undefined : osmEntity(entity);
56638                             }
56639
56640                             d.graph = coreGraph(_stack[0].graph).load(entities);
56641                             return d;
56642                         });
56643                     }
56644
56645                     var transform = _stack[_index].transform;
56646                     if (transform) {
56647                         context.map().transformEase(transform, 0);   // 0 = immediate, no easing
56648                     }
56649
56650                     if (loadComplete) {
56651                         dispatch$1.call('change');
56652                         dispatch$1.call('restore', this);
56653                     }
56654
56655                     return history;
56656                 },
56657
56658
56659                 lock: function() {
56660                     return lock.lock();
56661                 },
56662
56663
56664                 unlock: function() {
56665                     lock.unlock();
56666                 },
56667
56668
56669                 save: function() {
56670                     if (lock.locked() &&
56671                         // don't overwrite existing, unresolved changes
56672                         !_hasUnresolvedRestorableChanges) {
56673
56674                         corePreferences(getKey('saved_history'), history.toJSON() || null);
56675                     }
56676                     return history;
56677                 },
56678
56679
56680                 // delete the history version saved in localStorage
56681                 clearSaved: function() {
56682                     context.debouncedSave.cancel();
56683                     if (lock.locked()) {
56684                         _hasUnresolvedRestorableChanges = false;
56685                         corePreferences(getKey('saved_history'), null);
56686
56687                         // clear the changeset metadata associated with the saved history
56688                         corePreferences('comment', null);
56689                         corePreferences('hashtags', null);
56690                         corePreferences('source', null);
56691                     }
56692                     return history;
56693                 },
56694
56695
56696                 savedHistoryJSON: function() {
56697                     return corePreferences(getKey('saved_history'));
56698                 },
56699
56700
56701                 hasRestorableChanges: function() {
56702                     return _hasUnresolvedRestorableChanges;
56703                 },
56704
56705
56706                 // load history from a version stored in localStorage
56707                 restore: function() {
56708                     if (lock.locked()) {
56709                         _hasUnresolvedRestorableChanges = false;
56710                         var json = this.savedHistoryJSON();
56711                         if (json) history.fromJSON(json, true);
56712                     }
56713                 },
56714
56715
56716                 _getKey: getKey
56717
56718             };
56719
56720
56721             history.reset();
56722
56723             return utilRebind(history, dispatch$1, 'on');
56724         }
56725
56726         /**
56727          * Look for roads that can be connected to other roads with a short extension
56728          */
56729         function validationAlmostJunction(context) {
56730           const type = 'almost_junction';
56731           const EXTEND_TH_METERS = 5;
56732           const WELD_TH_METERS = 0.75;
56733           // Comes from considering bounding case of parallel ways
56734           const CLOSE_NODE_TH = EXTEND_TH_METERS - WELD_TH_METERS;
56735           // Comes from considering bounding case of perpendicular ways
56736           const SIG_ANGLE_TH = Math.atan(WELD_TH_METERS / EXTEND_TH_METERS);
56737
56738           function isHighway(entity) {
56739             return entity.type === 'way'
56740               && osmRoutableHighwayTagValues[entity.tags.highway];
56741           }
56742
56743           function isTaggedAsNotContinuing(node) {
56744             return node.tags.noexit === 'yes'
56745               || node.tags.amenity === 'parking_entrance'
56746               || (node.tags.entrance && node.tags.entrance !== 'no');
56747           }
56748
56749
56750           const validation = function checkAlmostJunction(entity, graph) {
56751             if (!isHighway(entity)) return [];
56752             if (entity.isDegenerate()) return [];
56753
56754             const tree = context.history().tree();
56755             const extendableNodeInfos = findConnectableEndNodesByExtension(entity);
56756
56757             let issues = [];
56758
56759             extendableNodeInfos.forEach(extendableNodeInfo => {
56760               issues.push(new validationIssue({
56761                 type,
56762                 subtype: 'highway-highway',
56763                 severity: 'warning',
56764                 message(context) {
56765                   const entity1 = context.hasEntity(this.entityIds[0]);
56766                   if (this.entityIds[0] === this.entityIds[2]) {
56767                     return entity1 ? _t('issues.almost_junction.self.message', {
56768                       feature: utilDisplayLabel(entity1, context.graph())
56769                     }) : '';
56770                   } else {
56771                     const entity2 = context.hasEntity(this.entityIds[2]);
56772                     return (entity1 && entity2) ? _t('issues.almost_junction.message', {
56773                       feature: utilDisplayLabel(entity1, context.graph()),
56774                       feature2: utilDisplayLabel(entity2, context.graph())
56775                     }) : '';
56776                   }
56777                 },
56778                 reference: showReference,
56779                 entityIds: [
56780                   entity.id,
56781                   extendableNodeInfo.node.id,
56782                   extendableNodeInfo.wid,
56783                 ],
56784                 loc: extendableNodeInfo.node.loc,
56785                 hash: JSON.stringify(extendableNodeInfo.node.loc),
56786                 data: {
56787                   midId: extendableNodeInfo.mid.id,
56788                   edge: extendableNodeInfo.edge,
56789                   cross_loc: extendableNodeInfo.cross_loc
56790                 },
56791                 dynamicFixes: makeFixes
56792               }));
56793             });
56794
56795             return issues;
56796
56797             function makeFixes(context) {
56798               let fixes = [new validationIssueFix({
56799                 icon: 'iD-icon-abutment',
56800                 title: _t('issues.fix.connect_features.title'),
56801                 onClick(context) {
56802                   const annotation = _t('issues.fix.connect_almost_junction.annotation');
56803                   const [, endNodeId, crossWayId] = this.issue.entityIds;
56804                   const midNode = context.entity(this.issue.data.midId);
56805                   const endNode = context.entity(endNodeId);
56806                   const crossWay = context.entity(crossWayId);
56807
56808                   // When endpoints are close, just join if resulting small change in angle (#7201)
56809                   const nearEndNodes = findNearbyEndNodes(endNode, crossWay);
56810                   if (nearEndNodes.length > 0) {
56811                     const collinear = findSmallJoinAngle(midNode, endNode, nearEndNodes);
56812                     if (collinear) {
56813                       context.perform(
56814                         actionMergeNodes([collinear.id, endNode.id], collinear.loc),
56815                         annotation
56816                       );
56817                       return;
56818                     }
56819                   }
56820
56821                   const targetEdge = this.issue.data.edge;
56822                   const crossLoc = this.issue.data.cross_loc;
56823                   const edgeNodes = [context.entity(targetEdge[0]), context.entity(targetEdge[1])];
56824                   const closestNodeInfo = geoSphericalClosestNode(edgeNodes, crossLoc);
56825
56826                   // already a point nearby, just connect to that
56827                   if (closestNodeInfo.distance < WELD_TH_METERS) {
56828                     context.perform(
56829                       actionMergeNodes([closestNodeInfo.node.id, endNode.id], closestNodeInfo.node.loc),
56830                       annotation
56831                     );
56832                   // else add the end node to the edge way
56833                   } else {
56834                     context.perform(
56835                       actionAddMidpoint({loc: crossLoc, edge: targetEdge}, endNode),
56836                       annotation
56837                     );
56838                   }
56839                 }
56840               })];
56841
56842               const node = context.hasEntity(this.entityIds[1]);
56843               if (node && !node.hasInterestingTags()) {
56844                 // node has no descriptive tags, suggest noexit fix
56845                 fixes.push(new validationIssueFix({
56846                   icon: 'maki-barrier',
56847                   title: _t('issues.fix.tag_as_disconnected.title'),
56848                   onClick(context) {
56849                     const nodeID = this.issue.entityIds[1];
56850                     const tags = Object.assign({}, context.entity(nodeID).tags);
56851                     tags.noexit = 'yes';
56852                     context.perform(
56853                       actionChangeTags(nodeID, tags),
56854                       _t('issues.fix.tag_as_disconnected.annotation')
56855                     );
56856                   }
56857                 }));
56858               }
56859
56860               return fixes;
56861             }
56862
56863             function showReference(selection) {
56864               selection.selectAll('.issue-reference')
56865                 .data([0])
56866                 .enter()
56867                 .append('div')
56868                 .attr('class', 'issue-reference')
56869                 .text(_t('issues.almost_junction.highway-highway.reference'));
56870             }
56871
56872             function isExtendableCandidate(node, way) {
56873               // can not accurately test vertices on tiles not downloaded from osm - #5938
56874               const osm = services.osm;
56875               if (osm && !osm.isDataLoaded(node.loc)) {
56876                 return false;
56877               }
56878               if (isTaggedAsNotContinuing(node) || graph.parentWays(node).length !== 1) {
56879                 return false;
56880               }
56881
56882               let occurences = 0;
56883               for (const index in way.nodes) {
56884                 if (way.nodes[index] === node.id) {
56885                   occurences += 1;
56886                   if (occurences > 1) {
56887                     return false;
56888                   }
56889                 }
56890               }
56891               return true;
56892             }
56893
56894             function findConnectableEndNodesByExtension(way) {
56895               let results = [];
56896               if (way.isClosed()) return results;
56897
56898               let testNodes;
56899               const indices = [0, way.nodes.length - 1];
56900               indices.forEach(nodeIndex => {
56901                 const nodeID = way.nodes[nodeIndex];
56902                 const node = graph.entity(nodeID);
56903
56904                 if (!isExtendableCandidate(node, way)) return;
56905
56906                 const connectionInfo = canConnectByExtend(way, nodeIndex);
56907                 if (!connectionInfo) return;
56908
56909                 testNodes = graph.childNodes(way).slice();   // shallow copy
56910                 testNodes[nodeIndex] = testNodes[nodeIndex].move(connectionInfo.cross_loc);
56911
56912                 // don't flag issue if connecting the ways would cause self-intersection
56913                 if (geoHasSelfIntersections(testNodes, nodeID)) return;
56914
56915                 results.push(connectionInfo);
56916               });
56917
56918               return results;
56919             }
56920
56921             function findNearbyEndNodes(node, way) {
56922               return [
56923                 way.nodes[0],
56924                 way.nodes[way.nodes.length - 1]
56925               ].map(d => graph.entity(d))
56926               .filter(d => {
56927                 // Node cannot be near to itself, but other endnode of same way could be
56928                 return d.id !== node.id
56929                   && geoSphericalDistance(node.loc, d.loc) <= CLOSE_NODE_TH;
56930               });
56931             }
56932
56933             function findSmallJoinAngle(midNode, tipNode, endNodes) {
56934               // Both nodes could be close, so want to join whichever is closest to collinear
56935               let joinTo;
56936               let minAngle = Infinity;
56937
56938               // Checks midNode -> tipNode -> endNode for collinearity
56939               endNodes.forEach(endNode => {
56940                 const a1 = geoAngle(midNode, tipNode, context.projection) + Math.PI;
56941                 const a2 = geoAngle(midNode, endNode, context.projection) + Math.PI;
56942                 const diff = Math.max(a1, a2) - Math.min(a1, a2);
56943
56944                 if (diff < minAngle) {
56945                   joinTo = endNode;
56946                   minAngle = diff;
56947                 }
56948               });
56949
56950               /* Threshold set by considering right angle triangle
56951               based on node joining threshold and extension distance */
56952               if (minAngle <= SIG_ANGLE_TH) return joinTo;
56953
56954               return null;
56955             }
56956
56957             function hasTag(tags, key) {
56958               return tags[key] !== undefined && tags[key] !== 'no';
56959             }
56960
56961             function canConnectWays(way, way2) {
56962
56963               // allow self-connections
56964               if (way.id === way2.id) return true;
56965
56966               // if one is bridge or tunnel, both must be bridge or tunnel
56967               if ((hasTag(way.tags, 'bridge') || hasTag(way2.tags, 'bridge')) &&
56968                 !(hasTag(way.tags, 'bridge') && hasTag(way2.tags, 'bridge'))) return false;
56969               if ((hasTag(way.tags, 'tunnel') || hasTag(way2.tags, 'tunnel')) &&
56970                 !(hasTag(way.tags, 'tunnel') && hasTag(way2.tags, 'tunnel'))) return false;
56971
56972               // must have equivalent layers and levels
56973               const layer1 = way.tags.layer || '0',
56974                 layer2 = way2.tags.layer || '0';
56975               if (layer1 !== layer2) return false;
56976
56977               const level1 = way.tags.level || '0',
56978                 level2 = way2.tags.level || '0';
56979               if (level1 !== level2) return false;
56980
56981               return true;
56982             }
56983
56984             function canConnectByExtend(way, endNodeIdx) {
56985               const tipNid = way.nodes[endNodeIdx];  // the 'tip' node for extension point
56986               const midNid = endNodeIdx === 0 ? way.nodes[1] : way.nodes[way.nodes.length - 2];  // the other node of the edge
56987               const tipNode = graph.entity(tipNid);
56988               const midNode = graph.entity(midNid);
56989               const lon = tipNode.loc[0];
56990               const lat = tipNode.loc[1];
56991               const lon_range = geoMetersToLon(EXTEND_TH_METERS, lat) / 2;
56992               const lat_range = geoMetersToLat(EXTEND_TH_METERS) / 2;
56993               const queryExtent = geoExtent([
56994                 [lon - lon_range, lat - lat_range],
56995                 [lon + lon_range, lat + lat_range]
56996               ]);
56997
56998               // first, extend the edge of [midNode -> tipNode] by EXTEND_TH_METERS and find the "extended tip" location
56999               const edgeLen = geoSphericalDistance(midNode.loc, tipNode.loc);
57000               const t = EXTEND_TH_METERS / edgeLen + 1.0;
57001               const extTipLoc = geoVecInterp(midNode.loc, tipNode.loc, t);
57002
57003               // then, check if the extension part [tipNode.loc -> extTipLoc] intersects any other ways
57004               const segmentInfos = tree.waySegments(queryExtent, graph);
57005               for (let i = 0; i < segmentInfos.length; i++) {
57006                 let segmentInfo = segmentInfos[i];
57007
57008                 let way2 = graph.entity(segmentInfo.wayId);
57009
57010                 if (!isHighway(way2)) continue;
57011
57012                 if (!canConnectWays(way, way2)) continue;
57013
57014                 let nAid = segmentInfo.nodes[0],
57015                   nBid = segmentInfo.nodes[1];
57016
57017                 if (nAid === tipNid || nBid === tipNid) continue;
57018
57019                 let nA = graph.entity(nAid),
57020                   nB = graph.entity(nBid);
57021                 let crossLoc = geoLineIntersection([tipNode.loc, extTipLoc], [nA.loc, nB.loc]);
57022                 if (crossLoc) {
57023                   return {
57024                     mid: midNode,
57025                     node: tipNode,
57026                     wid: way2.id,
57027                     edge: [nA.id, nB.id],
57028                     cross_loc: crossLoc
57029                   };
57030                 }
57031               }
57032               return null;
57033             }
57034           };
57035
57036           validation.type = type;
57037
57038           return validation;
57039         }
57040
57041         function validationCloseNodes(context) {
57042             var type = 'close_nodes';
57043
57044             var pointThresholdMeters = 0.2;
57045
57046             var validation = function(entity, graph) {
57047                 if (entity.type === 'node') {
57048                     return getIssuesForNode(entity);
57049                 } else if (entity.type === 'way') {
57050                     return getIssuesForWay(entity);
57051                 }
57052                 return [];
57053
57054                 function getIssuesForNode(node) {
57055                     var parentWays = graph.parentWays(node);
57056                     if (parentWays.length) {
57057                         return getIssuesForVertex(node, parentWays);
57058                     } else {
57059                         return getIssuesForDetachedPoint(node);
57060                     }
57061                 }
57062
57063                 function wayTypeFor(way) {
57064
57065                     if (way.tags.boundary && way.tags.boundary !== 'no') return 'boundary';
57066                     if (way.tags.indoor && way.tags.indoor !== 'no') return 'indoor';
57067                     if ((way.tags.building && way.tags.building !== 'no') ||
57068                         (way.tags['building:part'] && way.tags['building:part'] !== 'no')) return 'building';
57069                     if (osmPathHighwayTagValues[way.tags.highway]) return 'path';
57070
57071                     var parentRelations = graph.parentRelations(way);
57072                     for (var i in parentRelations) {
57073                         var relation = parentRelations[i];
57074
57075                         if (relation.tags.type === 'boundary') return 'boundary';
57076
57077                         if (relation.isMultipolygon()) {
57078                             if (relation.tags.indoor && relation.tags.indoor !== 'no') return 'indoor';
57079                             if ((relation.tags.building && relation.tags.building !== 'no') ||
57080                                 (relation.tags['building:part'] && relation.tags['building:part'] !== 'no')) return 'building';
57081                         }
57082                     }
57083
57084                     return 'other';
57085                 }
57086
57087                 function shouldCheckWay(way) {
57088
57089                     // don't flag issues where merging would create degenerate ways
57090                     if (way.nodes.length <= 2 ||
57091                         (way.isClosed() && way.nodes.length <= 4)) return false;
57092
57093                     var bbox = way.extent(graph).bbox();
57094                     var hypotenuseMeters = geoSphericalDistance([bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]);
57095                     // don't flag close nodes in very small ways
57096                     if (hypotenuseMeters < 1.5) return false;
57097
57098                     return true;
57099                 }
57100
57101                 function getIssuesForWay(way) {
57102                     if (!shouldCheckWay(way)) return [];
57103
57104                     var issues = [],
57105                         nodes = graph.childNodes(way);
57106                     for (var i = 0; i < nodes.length - 1; i++) {
57107                         var node1 = nodes[i];
57108                         var node2 = nodes[i+1];
57109
57110                         var issue = getWayIssueIfAny(node1, node2, way);
57111                         if (issue) issues.push(issue);
57112                     }
57113                     return issues;
57114                 }
57115
57116                 function getIssuesForVertex(node, parentWays) {
57117                     var issues = [];
57118
57119                     function checkForCloseness(node1, node2, way) {
57120                         var issue = getWayIssueIfAny(node1, node2, way);
57121                         if (issue) issues.push(issue);
57122                     }
57123
57124                     for (var i = 0; i < parentWays.length; i++) {
57125                         var parentWay = parentWays[i];
57126
57127                         if (!shouldCheckWay(parentWay)) continue;
57128
57129                         var lastIndex = parentWay.nodes.length - 1;
57130                         for (var j = 0; j < parentWay.nodes.length; j++) {
57131                             if (j !== 0) {
57132                                 if (parentWay.nodes[j-1] === node.id) {
57133                                     checkForCloseness(node, graph.entity(parentWay.nodes[j]), parentWay);
57134                                 }
57135                             }
57136                             if (j !== lastIndex) {
57137                                 if (parentWay.nodes[j+1] === node.id) {
57138                                     checkForCloseness(graph.entity(parentWay.nodes[j]), node, parentWay);
57139                                 }
57140                             }
57141                         }
57142                     }
57143                     return issues;
57144                 }
57145
57146                 function thresholdMetersForWay(way) {
57147                     if (!shouldCheckWay(way)) return 0;
57148
57149                     var wayType = wayTypeFor(way);
57150
57151                     // don't flag boundaries since they might be highly detailed and can't be easily verified
57152                     if (wayType === 'boundary') return 0;
57153                     // expect some features to be mapped with higher levels of detail
57154                     if (wayType === 'indoor') return 0.01;
57155                     if (wayType === 'building') return 0.05;
57156                     if (wayType === 'path') return 0.1;
57157                     return 0.2;
57158                 }
57159
57160                 function getIssuesForDetachedPoint(node) {
57161
57162                     var issues = [];
57163
57164                     var lon = node.loc[0];
57165                     var lat = node.loc[1];
57166                     var lon_range = geoMetersToLon(pointThresholdMeters, lat) / 2;
57167                     var lat_range = geoMetersToLat(pointThresholdMeters) / 2;
57168                     var queryExtent = geoExtent([
57169                         [lon - lon_range, lat - lat_range],
57170                         [lon + lon_range, lat + lat_range]
57171                     ]);
57172
57173                     var intersected = context.history().tree().intersects(queryExtent, graph);
57174                     for (var j = 0; j < intersected.length; j++) {
57175                         var nearby = intersected[j];
57176
57177                         if (nearby.id === node.id) continue;
57178                         if (nearby.type !== 'node' || nearby.geometry(graph) !== 'point') continue;
57179
57180                         if (nearby.loc === node.loc ||
57181                             geoSphericalDistance(node.loc, nearby.loc) < pointThresholdMeters) {
57182
57183                             // allow very close points if tags indicate the z-axis might vary
57184                             var zAxisKeys = { layer: true, level: true, 'addr:housenumber': true, 'addr:unit': true };
57185                             var zAxisDifferentiates = false;
57186                             for (var key in zAxisKeys) {
57187                                 var nodeValue = node.tags[key] || '0';
57188                                 var nearbyValue = nearby.tags[key] || '0';
57189                                 if (nodeValue !== nearbyValue) {
57190                                     zAxisDifferentiates = true;
57191                                     break;
57192                                 }
57193                             }
57194                             if (zAxisDifferentiates) continue;
57195
57196                             issues.push(new validationIssue({
57197                                 type: type,
57198                                 subtype: 'detached',
57199                                 severity: 'warning',
57200                                 message: function(context) {
57201                                     var entity = context.hasEntity(this.entityIds[0]),
57202                                         entity2 = context.hasEntity(this.entityIds[1]);
57203                                     return (entity && entity2) ? _t('issues.close_nodes.detached.message', {
57204                                         feature: utilDisplayLabel(entity, context.graph()),
57205                                         feature2: utilDisplayLabel(entity2, context.graph())
57206                                     }) : '';
57207                                 },
57208                                 reference: showReference,
57209                                 entityIds: [node.id, nearby.id],
57210                                 dynamicFixes: function() {
57211                                     return [
57212                                         new validationIssueFix({
57213                                             icon: 'iD-operation-disconnect',
57214                                             title: _t('issues.fix.move_points_apart.title')
57215                                         }),
57216                                         new validationIssueFix({
57217                                             icon: 'iD-icon-layers',
57218                                             title: _t('issues.fix.use_different_layers_or_levels.title')
57219                                         })
57220                                     ];
57221                                 }
57222                             }));
57223                         }
57224                     }
57225
57226                     return issues;
57227
57228                     function showReference(selection) {
57229                         var referenceText = _t('issues.close_nodes.detached.reference');
57230                         selection.selectAll('.issue-reference')
57231                             .data([0])
57232                             .enter()
57233                             .append('div')
57234                             .attr('class', 'issue-reference')
57235                             .text(referenceText);
57236                     }
57237                 }
57238
57239                 function getWayIssueIfAny(node1, node2, way) {
57240                     if (node1.id === node2.id ||
57241                         (node1.hasInterestingTags() && node2.hasInterestingTags())) {
57242                         return null;
57243                     }
57244
57245                     if (node1.loc !== node2.loc) {
57246                         var parentWays1 = graph.parentWays(node1);
57247                         var parentWays2 = new Set(graph.parentWays(node2));
57248
57249                         var sharedWays = parentWays1.filter(function(parentWay) {
57250                             return parentWays2.has(parentWay);
57251                         });
57252
57253                         var thresholds = sharedWays.map(function(parentWay) {
57254                             return thresholdMetersForWay(parentWay);
57255                         });
57256
57257                         var threshold = Math.min(...thresholds);
57258                         var distance = geoSphericalDistance(node1.loc, node2.loc);
57259                         if (distance > threshold) return null;
57260                     }
57261
57262                     return new validationIssue({
57263                         type: type,
57264                         subtype: 'vertices',
57265                         severity: 'warning',
57266                         message: function(context) {
57267                             var entity = context.hasEntity(this.entityIds[0]);
57268                             return entity ? _t('issues.close_nodes.message', { way: utilDisplayLabel(entity, context.graph()) }) : '';
57269                         },
57270                         reference: showReference,
57271                         entityIds: [way.id, node1.id, node2.id],
57272                         loc: node1.loc,
57273                         dynamicFixes: function() {
57274                             return [
57275                                 new validationIssueFix({
57276                                     icon: 'iD-icon-plus',
57277                                     title: _t('issues.fix.merge_points.title'),
57278                                     onClick: function(context) {
57279                                         var entityIds = this.issue.entityIds;
57280                                         var action = actionMergeNodes([entityIds[1], entityIds[2]]);
57281                                         context.perform(action, _t('issues.fix.merge_close_vertices.annotation'));
57282                                     }
57283                                 }),
57284                                 new validationIssueFix({
57285                                     icon: 'iD-operation-disconnect',
57286                                     title: _t('issues.fix.move_points_apart.title')
57287                                 })
57288                             ];
57289                         }
57290                     });
57291
57292                     function showReference(selection) {
57293                         var referenceText = _t('issues.close_nodes.reference');
57294                         selection.selectAll('.issue-reference')
57295                             .data([0])
57296                             .enter()
57297                             .append('div')
57298                             .attr('class', 'issue-reference')
57299                             .text(referenceText);
57300                     }
57301                 }
57302
57303             };
57304
57305
57306             validation.type = type;
57307
57308             return validation;
57309         }
57310
57311         function validationCrossingWays(context) {
57312             var type = 'crossing_ways';
57313
57314             // returns the way or its parent relation, whichever has a useful feature type
57315             function getFeatureWithFeatureTypeTagsForWay(way, graph) {
57316                 if (getFeatureType(way, graph) === null) {
57317                     // if the way doesn't match a feature type, check its parent relations
57318                     var parentRels = graph.parentRelations(way);
57319                     for (var i = 0; i < parentRels.length; i++) {
57320                         var rel = parentRels[i];
57321                         if (getFeatureType(rel, graph) !== null) {
57322                             return rel;
57323                         }
57324                     }
57325                 }
57326                 return way;
57327             }
57328
57329
57330             function hasTag(tags, key) {
57331                 return tags[key] !== undefined && tags[key] !== 'no';
57332             }
57333
57334             function taggedAsIndoor(tags) {
57335                 return hasTag(tags, 'indoor') ||
57336                     hasTag(tags, 'level') ||
57337                     tags.highway === 'corridor';
57338             }
57339
57340             function allowsBridge(featureType) {
57341                 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
57342             }
57343             function allowsTunnel(featureType) {
57344                 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
57345             }
57346
57347
57348             function getFeatureTypeForCrossingCheck(way, graph) {
57349                 var feature = getFeatureWithFeatureTypeTagsForWay(way, graph);
57350                 return getFeatureType(feature, graph);
57351             }
57352
57353             // discard
57354             var ignoredBuildings = {
57355                 demolished: true, dismantled: true, proposed: true, razed: true
57356             };
57357
57358
57359             function getFeatureType(entity, graph) {
57360
57361                 var geometry = entity.geometry(graph);
57362                 if (geometry !== 'line' && geometry !== 'area') return null;
57363
57364                 var tags = entity.tags;
57365
57366                 if (hasTag(tags, 'building') && !ignoredBuildings[tags.building]) return 'building';
57367                 if (hasTag(tags, 'highway') && osmRoutableHighwayTagValues[tags.highway]) return 'highway';
57368
57369                 // don't check railway or waterway areas
57370                 if (geometry !== 'line') return null;
57371
57372                 if (hasTag(tags, 'railway') && osmRailwayTrackTagValues[tags.railway]) return 'railway';
57373                 if (hasTag(tags, 'waterway') && osmFlowingWaterwayTagValues[tags.waterway]) return 'waterway';
57374
57375                 return null;
57376             }
57377
57378
57379             function isLegitCrossing(way1, featureType1, way2, featureType2) {
57380                 var tags1 = way1.tags;
57381                 var tags2 = way2.tags;
57382
57383                 // assume 0 by default
57384                 var level1 = tags1.level || '0';
57385                 var level2 = tags2.level || '0';
57386
57387                 if (taggedAsIndoor(tags1) && taggedAsIndoor(tags2) && level1 !== level2) {
57388                     // assume features don't interact if they're indoor on different levels
57389                     return true;
57390                 }
57391
57392                 // assume 0 by default; don't use way.layer() since we account for structures here
57393                 var layer1 = tags1.layer || '0';
57394                 var layer2 = tags2.layer || '0';
57395
57396                 if (allowsBridge(featureType1) && allowsBridge(featureType2)) {
57397                     if (hasTag(tags1, 'bridge') && !hasTag(tags2, 'bridge')) return true;
57398                     if (!hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge')) return true;
57399                     // crossing bridges must use different layers
57400                     if (hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge') && layer1 !== layer2) return true;
57401                 } else if (allowsBridge(featureType1) && hasTag(tags1, 'bridge')) return true;
57402                 else if (allowsBridge(featureType2) && hasTag(tags2, 'bridge')) return true;
57403
57404                 if (allowsTunnel(featureType1) && allowsTunnel(featureType2)) {
57405                     if (hasTag(tags1, 'tunnel') && !hasTag(tags2, 'tunnel')) return true;
57406                     if (!hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel')) return true;
57407                     // crossing tunnels must use different layers
57408                     if (hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel') && layer1 !== layer2) return true;
57409                 } else if (allowsTunnel(featureType1) && hasTag(tags1, 'tunnel')) return true;
57410                 else if (allowsTunnel(featureType2) && hasTag(tags2, 'tunnel')) return true;
57411
57412                 // don't flag crossing waterways and pier/highways
57413                 if (featureType1 === 'waterway' && featureType2 === 'highway' && tags2.man_made === 'pier') return true;
57414                 if (featureType2 === 'waterway' && featureType1 === 'highway' && tags1.man_made === 'pier') return true;
57415
57416                 if (featureType1 === 'building' || featureType2 === 'building') {
57417                     // for building crossings, different layers are enough
57418                     if (layer1 !== layer2) return true;
57419                 }
57420                 return false;
57421             }
57422
57423
57424             // highway values for which we shouldn't recommend connecting to waterways
57425             var highwaysDisallowingFords = {
57426                 motorway: true, motorway_link: true, trunk: true, trunk_link: true,
57427                 primary: true, primary_link: true, secondary: true, secondary_link: true
57428             };
57429             var nonCrossingHighways = { track: true };
57430
57431             function tagsForConnectionNodeIfAllowed(entity1, entity2, graph) {
57432                 var featureType1 = getFeatureType(entity1, graph);
57433                 var featureType2 = getFeatureType(entity2, graph);
57434
57435                 var geometry1 = entity1.geometry(graph);
57436                 var geometry2 = entity2.geometry(graph);
57437                 var bothLines = geometry1 === 'line' && geometry2 === 'line';
57438
57439                 if (featureType1 === featureType2) {
57440                     if (featureType1 === 'highway') {
57441                         var entity1IsPath = osmPathHighwayTagValues[entity1.tags.highway];
57442                         var entity2IsPath = osmPathHighwayTagValues[entity2.tags.highway];
57443                         if ((entity1IsPath || entity2IsPath) && entity1IsPath !== entity2IsPath) {
57444                             // one feature is a path but not both
57445
57446                             var roadFeature = entity1IsPath ? entity2 : entity1;
57447                             if (nonCrossingHighways[roadFeature.tags.highway]) {
57448                                 // don't mark path connections with certain roads as crossings
57449                                 return {};
57450                             }
57451                             var pathFeature = entity1IsPath ? entity1 : entity2;
57452                             if (['marked', 'unmarked'].indexOf(pathFeature.tags.crossing) !== -1) {
57453                                 // if the path is a crossing, match the crossing type
57454                                 return bothLines ? { highway: 'crossing', crossing: pathFeature.tags.crossing } : {};
57455                             }
57456                             // don't add a `crossing` subtag to ambiguous crossings
57457                             return bothLines ? { highway: 'crossing' } : {};
57458                         }
57459                         return {};
57460                     }
57461                     if (featureType1 === 'waterway') return {};
57462                     if (featureType1 === 'railway') return {};
57463
57464                 } else {
57465                     var featureTypes = [featureType1, featureType2];
57466                     if (featureTypes.indexOf('highway') !== -1) {
57467                         if (featureTypes.indexOf('railway') !== -1) {
57468                             if (osmPathHighwayTagValues[entity1.tags.highway] ||
57469                                 osmPathHighwayTagValues[entity2.tags.highway]) {
57470                                 // path-rail connections use this tag
57471                                 return bothLines ? { railway: 'crossing' } : {};
57472                             } else {
57473                                 // road-rail connections use this tag
57474                                 return bothLines ? { railway: 'level_crossing' } : {};
57475                             }
57476                         }
57477
57478                         if (featureTypes.indexOf('waterway') !== -1) {
57479                             // do not allow fords on structures
57480                             if (hasTag(entity1.tags, 'tunnel') && hasTag(entity2.tags, 'tunnel')) return null;
57481                             if (hasTag(entity1.tags, 'bridge') && hasTag(entity2.tags, 'bridge')) return null;
57482
57483                             if (highwaysDisallowingFords[entity1.tags.highway] ||
57484                                 highwaysDisallowingFords[entity2.tags.highway]) {
57485                                 // do not allow fords on major highways
57486                                 return null;
57487                             }
57488                             return bothLines ? { ford: 'yes' } : {};
57489                         }
57490                     }
57491                 }
57492                 return null;
57493             }
57494
57495
57496             function findCrossingsByWay(way1, graph, tree) {
57497                 var edgeCrossInfos = [];
57498                 if (way1.type !== 'way') return edgeCrossInfos;
57499
57500                 var way1FeatureType = getFeatureTypeForCrossingCheck(way1, graph);
57501                 if (way1FeatureType === null) return edgeCrossInfos;
57502
57503                 var checkedSingleCrossingWays = {};
57504
57505                 // declare vars ahead of time to reduce garbage collection
57506                 var i, j;
57507                 var extent;
57508                 var n1, n2, nA, nB, nAId, nBId;
57509                 var segment1, segment2;
57510                 var oneOnly;
57511                 var segmentInfos, segment2Info, way2, way2FeatureType;
57512                 var way1Nodes = graph.childNodes(way1);
57513                 var comparedWays = {};
57514                 for (i = 0; i < way1Nodes.length - 1; i++) {
57515                     n1 = way1Nodes[i];
57516                     n2 = way1Nodes[i + 1];
57517                     extent = geoExtent([
57518                         [
57519                             Math.min(n1.loc[0], n2.loc[0]),
57520                             Math.min(n1.loc[1], n2.loc[1])
57521                         ],
57522                         [
57523                             Math.max(n1.loc[0], n2.loc[0]),
57524                             Math.max(n1.loc[1], n2.loc[1])
57525                         ]
57526                     ]);
57527
57528                     // Optimize by only checking overlapping segments, not every segment
57529                     // of overlapping ways
57530                     segmentInfos = tree.waySegments(extent, graph);
57531
57532                     for (j = 0; j < segmentInfos.length; j++) {
57533                         segment2Info = segmentInfos[j];
57534
57535                         // don't check for self-intersection in this validation
57536                         if (segment2Info.wayId === way1.id) continue;
57537
57538                         // skip if this way was already checked and only one issue is needed
57539                         if (checkedSingleCrossingWays[segment2Info.wayId]) continue;
57540
57541                         // mark this way as checked even if there are no crossings
57542                         comparedWays[segment2Info.wayId] = true;
57543
57544                         way2 = graph.hasEntity(segment2Info.wayId);
57545                         if (!way2) continue;
57546
57547                         // only check crossing highway, waterway, building, and railway
57548                         way2FeatureType = getFeatureTypeForCrossingCheck(way2, graph);
57549                         if (way2FeatureType === null ||
57550                             isLegitCrossing(way1, way1FeatureType, way2, way2FeatureType)) {
57551                             continue;
57552                         }
57553
57554                         // create only one issue for building crossings
57555                         oneOnly = way1FeatureType === 'building' || way2FeatureType === 'building';
57556
57557                         nAId = segment2Info.nodes[0];
57558                         nBId = segment2Info.nodes[1];
57559                         if (nAId === n1.id || nAId === n2.id ||
57560                             nBId === n1.id || nBId === n2.id) {
57561                             // n1 or n2 is a connection node; skip
57562                             continue;
57563                         }
57564                         nA = graph.hasEntity(nAId);
57565                         if (!nA) continue;
57566                         nB = graph.hasEntity(nBId);
57567                         if (!nB) continue;
57568
57569                         segment1 = [n1.loc, n2.loc];
57570                         segment2 = [nA.loc, nB.loc];
57571                         var point = geoLineIntersection(segment1, segment2);
57572                         if (point) {
57573                             edgeCrossInfos.push({
57574                                 wayInfos: [
57575                                     {
57576                                         way: way1,
57577                                         featureType: way1FeatureType,
57578                                         edge: [n1.id, n2.id]
57579                                     },
57580                                     {
57581                                         way: way2,
57582                                         featureType: way2FeatureType,
57583                                         edge: [nA.id, nB.id]
57584                                     }
57585                                 ],
57586                                 crossPoint: point
57587                             });
57588                             if (oneOnly) {
57589                                 checkedSingleCrossingWays[way2.id] = true;
57590                                 break;
57591                             }
57592                         }
57593                     }
57594                 }
57595                 return edgeCrossInfos;
57596             }
57597
57598
57599             function waysToCheck(entity, graph) {
57600                 var featureType = getFeatureType(entity, graph);
57601                 if (!featureType) return [];
57602
57603                 if (entity.type === 'way') {
57604                     return [entity];
57605                 } else if (entity.type === 'relation') {
57606                     return entity.members.reduce(function(array, member) {
57607                         if (member.type === 'way' &&
57608                             // only look at geometry ways
57609                             (!member.role || member.role === 'outer' || member.role === 'inner')) {
57610                             var entity = graph.hasEntity(member.id);
57611                             // don't add duplicates
57612                             if (entity && array.indexOf(entity) === -1) {
57613                                 array.push(entity);
57614                             }
57615                         }
57616                         return array;
57617                     }, []);
57618                 }
57619                 return [];
57620             }
57621
57622
57623             var validation = function checkCrossingWays(entity, graph) {
57624
57625                 var tree = context.history().tree();
57626
57627                 var ways = waysToCheck(entity, graph);
57628
57629                 var issues = [];
57630                 // declare these here to reduce garbage collection
57631                 var wayIndex, crossingIndex, crossings;
57632                 for (wayIndex in ways) {
57633                     crossings = findCrossingsByWay(ways[wayIndex], graph, tree);
57634                     for (crossingIndex in crossings) {
57635                         issues.push(createIssue(crossings[crossingIndex], graph));
57636                     }
57637                 }
57638                 return issues;
57639             };
57640
57641
57642             function createIssue(crossing, graph) {
57643
57644                 // use the entities with the tags that define the feature type
57645                 crossing.wayInfos.sort(function(way1Info, way2Info) {
57646                     var type1 = way1Info.featureType;
57647                     var type2 = way2Info.featureType;
57648                     if (type1 === type2) {
57649                         return utilDisplayLabel(way1Info.way, graph) > utilDisplayLabel(way2Info.way, graph);
57650                     } else if (type1 === 'waterway') {
57651                         return true;
57652                     } else if (type2 === 'waterway') {
57653                         return false;
57654                     }
57655                     return type1 < type2;
57656                 });
57657                 var entities = crossing.wayInfos.map(function(wayInfo) {
57658                     return getFeatureWithFeatureTypeTagsForWay(wayInfo.way, graph);
57659                 });
57660                 var edges = [crossing.wayInfos[0].edge, crossing.wayInfos[1].edge];
57661                 var featureTypes = [crossing.wayInfos[0].featureType, crossing.wayInfos[1].featureType];
57662
57663                 var connectionTags = tagsForConnectionNodeIfAllowed(entities[0], entities[1], graph);
57664
57665                 var featureType1 = crossing.wayInfos[0].featureType;
57666                 var featureType2 = crossing.wayInfos[1].featureType;
57667
57668                 var isCrossingIndoors = taggedAsIndoor(entities[0].tags) && taggedAsIndoor(entities[1].tags);
57669                 var isCrossingTunnels = allowsTunnel(featureType1) && hasTag(entities[0].tags, 'tunnel') &&
57670                                         allowsTunnel(featureType2) && hasTag(entities[1].tags, 'tunnel');
57671                 var isCrossingBridges = allowsBridge(featureType1) && hasTag(entities[0].tags, 'bridge') &&
57672                                         allowsBridge(featureType2) && hasTag(entities[1].tags, 'bridge');
57673
57674                 var subtype = [featureType1, featureType2].sort().join('-');
57675
57676                 var crossingTypeID = subtype;
57677
57678                 if (isCrossingIndoors) {
57679                     crossingTypeID = 'indoor-indoor';
57680                 } else if (isCrossingTunnels) {
57681                     crossingTypeID = 'tunnel-tunnel';
57682                 } else if (isCrossingBridges) {
57683                     crossingTypeID = 'bridge-bridge';
57684                 }
57685                 if (connectionTags && (isCrossingIndoors || isCrossingTunnels || isCrossingBridges)) {
57686                     crossingTypeID += '_connectable';
57687                 }
57688
57689                 return new validationIssue({
57690                     type: type,
57691                     subtype: subtype,
57692                     severity: 'warning',
57693                     message: function(context) {
57694                         var graph = context.graph();
57695                         var entity1 = graph.hasEntity(this.entityIds[0]),
57696                             entity2 = graph.hasEntity(this.entityIds[1]);
57697                         return (entity1 && entity2) ? _t('issues.crossing_ways.message', {
57698                             feature: utilDisplayLabel(entity1, graph),
57699                             feature2: utilDisplayLabel(entity2, graph)
57700                         }) : '';
57701                     },
57702                     reference: showReference,
57703                     entityIds: entities.map(function(entity) {
57704                         return entity.id;
57705                     }),
57706                     data: {
57707                         edges: edges,
57708                         featureTypes: featureTypes,
57709                         connectionTags: connectionTags
57710                     },
57711                     // differentiate based on the loc since two ways can cross multiple times
57712                     hash: crossing.crossPoint.toString() +
57713                         // if the edges change then so does the fix
57714                         edges.slice().sort(function(edge1, edge2) {
57715                             // order to assure hash is deterministic
57716                             return edge1[0] < edge2[0] ? -1 : 1;
57717                         }).toString() +
57718                         // ensure the correct connection tags are added in the fix
57719                         JSON.stringify(connectionTags),
57720                     loc: crossing.crossPoint,
57721                     dynamicFixes: function(context) {
57722                         var mode = context.mode();
57723                         if (!mode || mode.id !== 'select' || mode.selectedIDs().length !== 1) return [];
57724
57725                         var selectedIndex = this.entityIds[0] === mode.selectedIDs()[0] ? 0 : 1;
57726                         var selectedFeatureType = this.data.featureTypes[selectedIndex];
57727                         var otherFeatureType = this.data.featureTypes[selectedIndex === 0 ? 1 : 0];
57728
57729                         var fixes = [];
57730
57731                         if (connectionTags) {
57732                             fixes.push(makeConnectWaysFix(this.data.connectionTags));
57733                         }
57734
57735                         if (isCrossingIndoors) {
57736                             fixes.push(new validationIssueFix({
57737                                 icon: 'iD-icon-layers',
57738                                 title: _t('issues.fix.use_different_levels.title')
57739                             }));
57740                         } else if (isCrossingTunnels ||
57741                             isCrossingBridges ||
57742                             featureType1 === 'building' ||
57743                             featureType2 === 'building')  {
57744
57745                             fixes.push(makeChangeLayerFix('higher'));
57746                             fixes.push(makeChangeLayerFix('lower'));
57747
57748                         // can only add bridge/tunnel if both features are lines
57749                         } else if (context.graph().geometry(this.entityIds[0]) === 'line' &&
57750                             context.graph().geometry(this.entityIds[1]) === 'line') {
57751
57752                             // don't recommend adding bridges to waterways since they're uncommmon
57753                             if (allowsBridge(selectedFeatureType) && selectedFeatureType !== 'waterway') {
57754                                 fixes.push(makeAddBridgeOrTunnelFix('add_a_bridge', 'temaki-bridge', 'bridge'));
57755                             }
57756
57757                             // don't recommend adding tunnels under waterways since they're uncommmon
57758                             var skipTunnelFix = otherFeatureType === 'waterway' && selectedFeatureType !== 'waterway';
57759                             if (allowsTunnel(selectedFeatureType) && !skipTunnelFix) {
57760                                 fixes.push(makeAddBridgeOrTunnelFix('add_a_tunnel', 'temaki-tunnel', 'tunnel'));
57761                             }
57762                         }
57763
57764                         // repositioning the features is always an option
57765                         fixes.push(new validationIssueFix({
57766                             icon: 'iD-operation-move',
57767                             title: _t('issues.fix.reposition_features.title')
57768                         }));
57769
57770                         return fixes;
57771                     }
57772                 });
57773
57774                 function showReference(selection) {
57775                     selection.selectAll('.issue-reference')
57776                         .data([0])
57777                         .enter()
57778                         .append('div')
57779                         .attr('class', 'issue-reference')
57780                         .text(_t('issues.crossing_ways.' + crossingTypeID + '.reference'));
57781                 }
57782             }
57783
57784             function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel){
57785                 return new validationIssueFix({
57786                     icon: iconName,
57787                     title: _t('issues.fix.' + fixTitleID + '.title'),
57788                     onClick: function(context) {
57789                         var mode = context.mode();
57790                         if (!mode || mode.id !== 'select') return;
57791
57792                         var selectedIDs = mode.selectedIDs();
57793                         if (selectedIDs.length !== 1) return;
57794
57795                         var selectedWayID = selectedIDs[0];
57796                         if (!context.hasEntity(selectedWayID)) return;
57797
57798                         var resultWayIDs = [selectedWayID];
57799
57800                         var edge, crossedEdge, crossedWayID;
57801                         if (this.issue.entityIds[0] === selectedWayID) {
57802                             edge = this.issue.data.edges[0];
57803                             crossedEdge = this.issue.data.edges[1];
57804                             crossedWayID = this.issue.entityIds[1];
57805                         } else {
57806                             edge = this.issue.data.edges[1];
57807                             crossedEdge = this.issue.data.edges[0];
57808                             crossedWayID = this.issue.entityIds[0];
57809                         }
57810
57811                         var crossingLoc = this.issue.loc;
57812
57813                         var projection = context.projection;
57814
57815                         var action = function actionAddStructure(graph) {
57816
57817                             var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
57818
57819                             var crossedWay = graph.hasEntity(crossedWayID);
57820                             // use the explicit width of the crossed feature as the structure length, if available
57821                             var structLengthMeters = crossedWay && crossedWay.tags.width && parseFloat(crossedWay.tags.width);
57822                             if (!structLengthMeters) {
57823                                 // if no explicit width is set, approximate the width based on the tags
57824                                 structLengthMeters = crossedWay && crossedWay.impliedLineWidthMeters();
57825                             }
57826                             if (structLengthMeters) {
57827                                 if (getFeatureType(crossedWay, graph) === 'railway') {
57828                                     // bridges over railways are generally much longer than the rail bed itself, compensate
57829                                     structLengthMeters *= 2;
57830                                 }
57831                             } else {
57832                                 // should ideally never land here since all rail/water/road tags should have an implied width
57833                                 structLengthMeters = 8;
57834                             }
57835
57836                             var a1 = geoAngle(edgeNodes[0], edgeNodes[1], projection) + Math.PI;
57837                             var a2 = geoAngle(graph.entity(crossedEdge[0]), graph.entity(crossedEdge[1]), projection) + Math.PI;
57838                             var crossingAngle = Math.max(a1, a2) - Math.min(a1, a2);
57839                             if (crossingAngle > Math.PI) crossingAngle -= Math.PI;
57840                             // lengthen the structure to account for the angle of the crossing
57841                             structLengthMeters = ((structLengthMeters / 2) / Math.sin(crossingAngle)) * 2;
57842
57843                             // add padding since the structure must extend past the edges of the crossed feature
57844                             structLengthMeters += 4;
57845
57846                             // clamp the length to a reasonable range
57847                             structLengthMeters = Math.min(Math.max(structLengthMeters, 4), 50);
57848
57849                             function geomToProj(geoPoint) {
57850                                 return [
57851                                     geoLonToMeters(geoPoint[0], geoPoint[1]),
57852                                     geoLatToMeters(geoPoint[1])
57853                                 ];
57854                             }
57855                             function projToGeom(projPoint) {
57856                                 var lat = geoMetersToLat(projPoint[1]);
57857                                 return [
57858                                     geoMetersToLon(projPoint[0], lat),
57859                                     lat
57860                                 ];
57861                             }
57862
57863                             var projEdgeNode1 = geomToProj(edgeNodes[0].loc);
57864                             var projEdgeNode2 = geomToProj(edgeNodes[1].loc);
57865
57866                             var projectedAngle = geoVecAngle(projEdgeNode1, projEdgeNode2);
57867
57868                             var projectedCrossingLoc = geomToProj(crossingLoc);
57869                             var linearToSphericalMetersRatio = geoVecLength(projEdgeNode1, projEdgeNode2) /
57870                                 geoSphericalDistance(edgeNodes[0].loc, edgeNodes[1].loc);
57871
57872                             function locSphericalDistanceFromCrossingLoc(angle, distanceMeters) {
57873                                 var lengthSphericalMeters = distanceMeters * linearToSphericalMetersRatio;
57874                                 return projToGeom([
57875                                     projectedCrossingLoc[0] + Math.cos(angle) * lengthSphericalMeters,
57876                                     projectedCrossingLoc[1] + Math.sin(angle) * lengthSphericalMeters
57877                                 ]);
57878                             }
57879
57880                             var endpointLocGetter1 = function(lengthMeters) {
57881                                 return locSphericalDistanceFromCrossingLoc(projectedAngle, lengthMeters);
57882                             };
57883                             var endpointLocGetter2 = function(lengthMeters) {
57884                                 return locSphericalDistanceFromCrossingLoc(projectedAngle + Math.PI, lengthMeters);
57885                             };
57886
57887                             // avoid creating very short edges from splitting too close to another node
57888                             var minEdgeLengthMeters = 0.55;
57889
57890                             // decide where to bound the structure along the way, splitting as necessary
57891                             function determineEndpoint(edge, endNode, locGetter) {
57892                                 var newNode;
57893
57894                                 var idealLengthMeters = structLengthMeters / 2;
57895
57896                                 // distance between the crossing location and the end of the edge,
57897                                 // the maximum length of this side of the structure
57898                                 var crossingToEdgeEndDistance = geoSphericalDistance(crossingLoc, endNode.loc);
57899
57900                                 if (crossingToEdgeEndDistance - idealLengthMeters > minEdgeLengthMeters) {
57901                                     // the edge is long enough to insert a new node
57902
57903                                     // the loc that would result in the full expected length
57904                                     var idealNodeLoc = locGetter(idealLengthMeters);
57905
57906                                     newNode = osmNode();
57907                                     graph = actionAddMidpoint({ loc: idealNodeLoc, edge: edge }, newNode)(graph);
57908
57909                                 } else {
57910                                     var edgeCount = 0;
57911                                     endNode.parentIntersectionWays(graph).forEach(function(way) {
57912                                         way.nodes.forEach(function(nodeID) {
57913                                             if (nodeID === endNode.id) {
57914                                                 if ((endNode.id === way.first() && endNode.id !== way.last()) ||
57915                                                     (endNode.id === way.last() && endNode.id !== way.first())) {
57916                                                     edgeCount += 1;
57917                                                 } else {
57918                                                     edgeCount += 2;
57919                                                 }
57920                                             }
57921                                         });
57922                                     });
57923
57924                                     if (edgeCount >= 3) {
57925                                         // the end node is a junction, try to leave a segment
57926                                         // between it and the structure - #7202
57927
57928                                         var insetLength = crossingToEdgeEndDistance - minEdgeLengthMeters;
57929                                         if (insetLength > minEdgeLengthMeters) {
57930                                             var insetNodeLoc = locGetter(insetLength);
57931                                             newNode = osmNode();
57932                                             graph = actionAddMidpoint({ loc: insetNodeLoc, edge: edge }, newNode)(graph);
57933                                         }
57934                                     }
57935                                 }
57936
57937                                 // if the edge is too short to subdivide as desired, then
57938                                 // just bound the structure at the existing end node
57939                                 if (!newNode) newNode = endNode;
57940
57941                                 var splitAction = actionSplit(newNode.id)
57942                                     .limitWays(resultWayIDs); // only split selected or created ways
57943
57944                                 // do the split
57945                                 graph = splitAction(graph);
57946                                 if (splitAction.getCreatedWayIDs().length) {
57947                                     resultWayIDs.push(splitAction.getCreatedWayIDs()[0]);
57948                                 }
57949
57950                                 return newNode;
57951                             }
57952
57953                             var structEndNode1 = determineEndpoint(edge, edgeNodes[1], endpointLocGetter1);
57954                             var structEndNode2 = determineEndpoint([edgeNodes[0].id, structEndNode1.id], edgeNodes[0], endpointLocGetter2);
57955
57956                             var structureWay = resultWayIDs.map(function(id) {
57957                                 return graph.entity(id);
57958                             }).find(function(way) {
57959                                 return way.nodes.indexOf(structEndNode1.id) !== -1 &&
57960                                     way.nodes.indexOf(structEndNode2.id) !== -1;
57961                             });
57962
57963                             var tags = Object.assign({}, structureWay.tags); // copy tags
57964                             if (bridgeOrTunnel === 'bridge'){
57965                                 tags.bridge = 'yes';
57966                                 tags.layer = '1';
57967                             } else {
57968                                 var tunnelValue = 'yes';
57969                                 if (getFeatureType(structureWay, graph) === 'waterway') {
57970                                     // use `tunnel=culvert` for waterways by default
57971                                     tunnelValue = 'culvert';
57972                                 }
57973                                 tags.tunnel = tunnelValue;
57974                                 tags.layer = '-1';
57975                             }
57976                             // apply the structure tags to the way
57977                             graph = actionChangeTags(structureWay.id, tags)(graph);
57978                             return graph;
57979                         };
57980
57981                         context.perform(action, _t('issues.fix.' + fixTitleID + '.annotation'));
57982                         context.enter(modeSelect(context, resultWayIDs));
57983                     }
57984                 });
57985             }
57986
57987             function makeConnectWaysFix(connectionTags) {
57988
57989                 var fixTitleID = 'connect_features';
57990                 if (connectionTags.ford) {
57991                     fixTitleID = 'connect_using_ford';
57992                 }
57993
57994                 return new validationIssueFix({
57995                     icon: 'iD-icon-crossing',
57996                     title: _t('issues.fix.' + fixTitleID + '.title'),
57997                     onClick: function(context) {
57998                         var loc = this.issue.loc;
57999                         var connectionTags = this.issue.data.connectionTags;
58000                         var edges = this.issue.data.edges;
58001
58002                         context.perform(
58003                             function actionConnectCrossingWays(graph) {
58004                                 // create the new node for the points
58005                                 var node = osmNode({ loc: loc, tags: connectionTags });
58006                                 graph = graph.replace(node);
58007
58008                                 var nodesToMerge = [node.id];
58009                                 var mergeThresholdInMeters = 0.75;
58010
58011                                 edges.forEach(function(edge) {
58012                                     var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
58013                                     var closestNodeInfo = geoSphericalClosestNode(edgeNodes, loc);
58014                                     // if there is already a point nearby, use that
58015                                     if (closestNodeInfo.distance < mergeThresholdInMeters) {
58016                                         nodesToMerge.push(closestNodeInfo.node.id);
58017                                     // else add the new node to the way
58018                                     } else {
58019                                         graph = actionAddMidpoint({loc: loc, edge: edge}, node)(graph);
58020                                     }
58021                                 });
58022
58023                                 if (nodesToMerge.length > 1) {
58024                                     // if we're using nearby nodes, merge them with the new node
58025                                     graph = actionMergeNodes(nodesToMerge, loc)(graph);
58026                                 }
58027
58028                                 return graph;
58029                             },
58030                             _t('issues.fix.connect_crossing_features.annotation')
58031                         );
58032                     }
58033                 });
58034             }
58035
58036             function makeChangeLayerFix(higherOrLower) {
58037                 return new validationIssueFix({
58038                     icon: 'iD-icon-' + (higherOrLower === 'higher' ? 'up' : 'down'),
58039                     title: _t('issues.fix.tag_this_as_' + higherOrLower + '.title'),
58040                     onClick: function(context) {
58041
58042                         var mode = context.mode();
58043                         if (!mode || mode.id !== 'select') return;
58044
58045                         var selectedIDs = mode.selectedIDs();
58046                         if (selectedIDs.length !== 1) return;
58047
58048                         var selectedID = selectedIDs[0];
58049                         if (!this.issue.entityIds.some(function(entityId) {
58050                             return entityId === selectedID;
58051                         })) return;
58052
58053                         var entity = context.hasEntity(selectedID);
58054                         if (!entity) return;
58055
58056                         var tags = Object.assign({}, entity.tags);   // shallow copy
58057                         var layer = tags.layer && Number(tags.layer);
58058                         if (layer && !isNaN(layer)) {
58059                             if (higherOrLower === 'higher') {
58060                                 layer += 1;
58061                             } else {
58062                                 layer -= 1;
58063                             }
58064                         } else {
58065                             if (higherOrLower === 'higher') {
58066                                 layer = 1;
58067                             } else {
58068                                 layer = -1;
58069                             }
58070                         }
58071                         tags.layer = layer.toString();
58072                         context.perform(
58073                             actionChangeTags(entity.id, tags),
58074                             _t('operations.change_tags.annotation')
58075                         );
58076                     }
58077                 });
58078             }
58079
58080             validation.type = type;
58081
58082             return validation;
58083         }
58084
58085         function validationDisconnectedWay() {
58086             var type = 'disconnected_way';
58087
58088             function isTaggedAsHighway(entity) {
58089                 return osmRoutableHighwayTagValues[entity.tags.highway];
58090             }
58091
58092             var validation = function checkDisconnectedWay(entity, graph) {
58093
58094                 var routingIslandWays = routingIslandForEntity(entity);
58095                 if (!routingIslandWays) return [];
58096
58097                 return [new validationIssue({
58098                     type: type,
58099                     subtype: 'highway',
58100                     severity: 'warning',
58101                     message: function(context) {
58102                         if (this.entityIds.length === 1) {
58103                             var entity = context.hasEntity(this.entityIds[0]);
58104                             return entity ? _t('issues.disconnected_way.highway.message', { highway: utilDisplayLabel(entity, context.graph()) }) : '';
58105                         }
58106                         return _t('issues.disconnected_way.routable.message.multiple', { count: this.entityIds.length.toString() });
58107                     },
58108                     reference: showReference,
58109                     entityIds: Array.from(routingIslandWays).map(function(way) { return way.id; }),
58110                     dynamicFixes: makeFixes
58111                 })];
58112
58113
58114                 function makeFixes(context) {
58115
58116                     var fixes = [];
58117
58118                     var singleEntity = this.entityIds.length === 1 && context.hasEntity(this.entityIds[0]);
58119
58120                     if (singleEntity) {
58121
58122                         if (singleEntity.type === 'way' && !singleEntity.isClosed()) {
58123
58124                             var textDirection = _mainLocalizer.textDirection();
58125
58126                             var startFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.first(), 'start');
58127                             if (startFix) fixes.push(startFix);
58128
58129                             var endFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.last(), 'end');
58130                             if (endFix) fixes.push(endFix);
58131                         }
58132                         if (!fixes.length) {
58133                             fixes.push(new validationIssueFix({
58134                                 title: _t('issues.fix.connect_feature.title')
58135                             }));
58136                         }
58137
58138                         fixes.push(new validationIssueFix({
58139                             icon: 'iD-operation-delete',
58140                             title: _t('issues.fix.delete_feature.title'),
58141                             entityIds: [singleEntity.id],
58142                             onClick: function(context) {
58143                                 var id = this.issue.entityIds[0];
58144                                 var operation = operationDelete(context, [id]);
58145                                 if (!operation.disabled()) {
58146                                     operation();
58147                                 }
58148                             }
58149                         }));
58150                     } else {
58151                         fixes.push(new validationIssueFix({
58152                             title: _t('issues.fix.connect_features.title')
58153                         }));
58154                     }
58155
58156                     return fixes;
58157                 }
58158
58159
58160                 function showReference(selection) {
58161                     selection.selectAll('.issue-reference')
58162                         .data([0])
58163                         .enter()
58164                         .append('div')
58165                         .attr('class', 'issue-reference')
58166                         .text(_t('issues.disconnected_way.routable.reference'));
58167                 }
58168
58169                 function routingIslandForEntity(entity) {
58170
58171                     var routingIsland = new Set();  // the interconnected routable features
58172                     var waysToCheck = [];           // the queue of remaining routable ways to traverse
58173
58174                     function queueParentWays(node) {
58175                         graph.parentWays(node).forEach(function(parentWay) {
58176                             if (!routingIsland.has(parentWay) &&    // only check each feature once
58177                                 isRoutableWay(parentWay, false)) {  // only check routable features
58178                                 routingIsland.add(parentWay);
58179                                 waysToCheck.push(parentWay);
58180                             }
58181                         });
58182                     }
58183
58184                     if (entity.type === 'way' && isRoutableWay(entity, true)) {
58185
58186                         routingIsland.add(entity);
58187                         waysToCheck.push(entity);
58188
58189                     } else if (entity.type === 'node' && isRoutableNode(entity)) {
58190
58191                         routingIsland.add(entity);
58192                         queueParentWays(entity);
58193
58194                     } else {
58195                         // this feature isn't routable, cannot be a routing island
58196                         return null;
58197                     }
58198
58199                     while (waysToCheck.length) {
58200                         var wayToCheck = waysToCheck.pop();
58201                         var childNodes = graph.childNodes(wayToCheck);
58202                         for (var i in childNodes) {
58203                             var vertex = childNodes[i];
58204
58205                             if (isConnectedVertex(vertex)) {
58206                                 // found a link to the wider network, not a routing island
58207                                 return null;
58208                             }
58209
58210                             if (isRoutableNode(vertex)) {
58211                                 routingIsland.add(vertex);
58212                             }
58213
58214                             queueParentWays(vertex);
58215                         }
58216                     }
58217
58218                     // no network link found, this is a routing island, return its members
58219                     return routingIsland;
58220                 }
58221
58222                 function isConnectedVertex(vertex) {
58223                     // assume ways overlapping unloaded tiles are connected to the wider road network  - #5938
58224                     var osm = services.osm;
58225                     if (osm && !osm.isDataLoaded(vertex.loc)) return true;
58226
58227                     // entrances are considered connected
58228                     if (vertex.tags.entrance &&
58229                         vertex.tags.entrance !== 'no') return true;
58230                     if (vertex.tags.amenity === 'parking_entrance') return true;
58231
58232                     return false;
58233                 }
58234
58235                 function isRoutableNode(node) {
58236                     // treat elevators as distinct features in the highway network
58237                     if (node.tags.highway === 'elevator') return true;
58238                     return false;
58239                 }
58240
58241                 function isRoutableWay(way, ignoreInnerWays) {
58242                     if (isTaggedAsHighway(way) || way.tags.route === 'ferry') return true;
58243
58244                     return graph.parentRelations(way).some(function(parentRelation) {
58245                         if (parentRelation.tags.type === 'route' &&
58246                             parentRelation.tags.route === 'ferry') return true;
58247
58248                         if (parentRelation.isMultipolygon() &&
58249                             isTaggedAsHighway(parentRelation) &&
58250                             (!ignoreInnerWays || parentRelation.memberById(way.id).role !== 'inner')) return true;
58251                     });
58252                 }
58253
58254                 function makeContinueDrawingFixIfAllowed(textDirection, vertexID, whichEnd) {
58255                     var vertex = graph.hasEntity(vertexID);
58256                     if (!vertex || vertex.tags.noexit === 'yes') return null;
58257
58258                     var useLeftContinue = (whichEnd === 'start' && textDirection === 'ltr') ||
58259                         (whichEnd === 'end' && textDirection === 'rtl');
58260
58261                     return new validationIssueFix({
58262                         icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
58263                         title: _t('issues.fix.continue_from_' + whichEnd + '.title'),
58264                         entityIds: [vertexID],
58265                         onClick: function(context) {
58266                             var wayId = this.issue.entityIds[0];
58267                             var way = context.hasEntity(wayId);
58268                             var vertexId = this.entityIds[0];
58269                             var vertex = context.hasEntity(vertexId);
58270
58271                             if (!way || !vertex) return;
58272
58273                             // make sure the vertex is actually visible and editable
58274                             var map = context.map();
58275                             if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
58276                                 map.zoomToEase(vertex);
58277                             }
58278
58279                             context.enter(
58280                                 modeDrawLine(context, wayId, context.graph(), 'line', way.affix(vertexId), true)
58281                             );
58282                         }
58283                     });
58284                 }
58285
58286             };
58287
58288             validation.type = type;
58289
58290             return validation;
58291         }
58292
58293         function validationFormatting() {
58294             var type = 'invalid_format';
58295
58296             var validation = function(entity) {
58297                 var issues = [];
58298
58299                 function isValidEmail(email) {
58300                     // Emails in OSM are going to be official so they should be pretty simple
58301                     // Using negated lists to better support all possible unicode characters (#6494)
58302                     var valid_email = /^[^\(\)\\,":;<>@\[\]]+@[^\(\)\\,":;<>@\[\]\.]+(?:\.[a-z0-9-]+)*$/i;
58303
58304                     // An empty value is also acceptable
58305                     return (!email || valid_email.test(email));
58306                 }
58307                 /*
58308                 function isSchemePresent(url) {
58309                     var valid_scheme = /^https?:\/\//i;
58310                     return (!url || valid_scheme.test(url));
58311                 }
58312                 */
58313                 function showReferenceEmail(selection) {
58314                     selection.selectAll('.issue-reference')
58315                         .data([0])
58316                         .enter()
58317                         .append('div')
58318                         .attr('class', 'issue-reference')
58319                         .text(_t('issues.invalid_format.email.reference'));
58320                 }
58321                 /*
58322                 function showReferenceWebsite(selection) {
58323                     selection.selectAll('.issue-reference')
58324                         .data([0])
58325                         .enter()
58326                         .append('div')
58327                         .attr('class', 'issue-reference')
58328                         .text(t('issues.invalid_format.website.reference'));
58329                 }
58330
58331                 if (entity.tags.website) {
58332                     // Multiple websites are possible
58333                     // If ever we support ES6, arrow functions make this nicer
58334                     var websites = entity.tags.website
58335                         .split(';')
58336                         .map(function(s) { return s.trim(); })
58337                         .filter(function(x) { return !isSchemePresent(x); });
58338
58339                     if (websites.length) {
58340                         issues.push(new validationIssue({
58341                             type: type,
58342                             subtype: 'website',
58343                             severity: 'warning',
58344                             message: function(context) {
58345                                 var entity = context.hasEntity(this.entityIds[0]);
58346                                 return entity ? t('issues.invalid_format.website.message' + this.data,
58347                                     { feature: utilDisplayLabel(entity, context.graph()), site: websites.join(', ') }) : '';
58348                             },
58349                             reference: showReferenceWebsite,
58350                             entityIds: [entity.id],
58351                             hash: websites.join(),
58352                             data: (websites.length > 1) ? '_multi' : ''
58353                         }));
58354                     }
58355                 }
58356                 */
58357                 if (entity.tags.email) {
58358                     // Multiple emails are possible
58359                     var emails = entity.tags.email
58360                         .split(';')
58361                         .map(function(s) { return s.trim(); })
58362                         .filter(function(x) { return !isValidEmail(x); });
58363
58364                     if (emails.length) {
58365                         issues.push(new validationIssue({
58366                             type: type,
58367                             subtype: 'email',
58368                             severity: 'warning',
58369                             message: function(context) {
58370                                 var entity = context.hasEntity(this.entityIds[0]);
58371                                 return entity ? _t('issues.invalid_format.email.message' + this.data,
58372                                     { feature: utilDisplayLabel(entity, context.graph()), email: emails.join(', ') }) : '';
58373                             },
58374                             reference: showReferenceEmail,
58375                             entityIds: [entity.id],
58376                             hash: emails.join(),
58377                             data: (emails.length > 1) ? '_multi' : ''
58378                         }));
58379                     }
58380                 }
58381
58382                 return issues;
58383             };
58384
58385             validation.type = type;
58386
58387             return validation;
58388         }
58389
58390         function validationHelpRequest(context) {
58391             var type = 'help_request';
58392
58393             var validation = function checkFixmeTag(entity) {
58394
58395                 if (!entity.tags.fixme) return [];
58396
58397                 // don't flag fixmes on features added by the user
58398                 if (entity.version === undefined) return [];
58399
58400                 if (entity.v !== undefined) {
58401                     var baseEntity = context.history().base().hasEntity(entity.id);
58402                     // don't flag fixmes added by the user on existing features
58403                     if (!baseEntity || !baseEntity.tags.fixme) return [];
58404                 }
58405
58406                 return [new validationIssue({
58407                     type: type,
58408                     subtype: 'fixme_tag',
58409                     severity: 'warning',
58410                     message: function(context) {
58411                         var entity = context.hasEntity(this.entityIds[0]);
58412                         return entity ? _t('issues.fixme_tag.message', { feature: utilDisplayLabel(entity, context.graph()) }) : '';
58413                     },
58414                     dynamicFixes: function() {
58415                         return [
58416                             new validationIssueFix({
58417                                 title: _t('issues.fix.address_the_concern.title')
58418                             })
58419                         ];
58420                     },
58421                     reference: showReference,
58422                     entityIds: [entity.id]
58423                 })];
58424
58425                 function showReference(selection) {
58426                     selection.selectAll('.issue-reference')
58427                         .data([0])
58428                         .enter()
58429                         .append('div')
58430                         .attr('class', 'issue-reference')
58431                         .text(_t('issues.fixme_tag.reference'));
58432                 }
58433             };
58434
58435             validation.type = type;
58436
58437             return validation;
58438         }
58439
58440         function validationImpossibleOneway() {
58441             var type = 'impossible_oneway';
58442
58443             var validation = function checkImpossibleOneway(entity, graph) {
58444
58445                 if (entity.type !== 'way' || entity.geometry(graph) !== 'line') return [];
58446
58447                 if (entity.isClosed()) return [];
58448
58449                 if (!typeForWay(entity)) return [];
58450
58451                 if (!isOneway(entity)) return [];
58452
58453                 var firstIssues = issuesForNode(entity, entity.first());
58454                 var lastIssues = issuesForNode(entity, entity.last());
58455
58456                 return firstIssues.concat(lastIssues);
58457
58458                 function typeForWay(way) {
58459                     if (way.geometry(graph) !== 'line') return null;
58460
58461                     if (osmRoutableHighwayTagValues[way.tags.highway]) return 'highway';
58462                     if (osmFlowingWaterwayTagValues[way.tags.waterway]) return 'waterway';
58463                     return null;
58464                 }
58465
58466                 function isOneway(way) {
58467                     if (way.tags.oneway === 'yes') return true;
58468                     if (way.tags.oneway) return false;
58469
58470                     for (var key in way.tags) {
58471                         if (osmOneWayTags[key] && osmOneWayTags[key][way.tags[key]]) {
58472                             return true;
58473                         }
58474                     }
58475                     return false;
58476                 }
58477
58478                 function nodeOccursMoreThanOnce(way, nodeID) {
58479                     var occurences = 0;
58480                     for (var index in way.nodes) {
58481                         if (way.nodes[index] === nodeID) {
58482                             occurences += 1;
58483                             if (occurences > 1) return true;
58484                         }
58485                     }
58486                     return false;
58487                 }
58488
58489                 function isConnectedViaOtherTypes(way, node) {
58490
58491                     var wayType = typeForWay(way);
58492
58493                     if (wayType === 'highway') {
58494                         // entrances are considered connected
58495                         if (node.tags.entrance && node.tags.entrance !== 'no') return true;
58496                         if (node.tags.amenity === 'parking_entrance') return true;
58497                     } else if (wayType === 'waterway') {
58498                         if (node.id === way.first()) {
58499                             // multiple waterways may start at the same spring
58500                             if (node.tags.natural === 'spring') return true;
58501                         } else {
58502                             // multiple waterways may end at the same drain
58503                             if (node.tags.manhole === 'drain') return true;
58504                         }
58505                     }
58506
58507                     return graph.parentWays(node).some(function(parentWay) {
58508                         if (parentWay.id === way.id) return false;
58509
58510                         if (wayType === 'highway') {
58511
58512                             // allow connections to highway areas
58513                             if (parentWay.geometry(graph) === 'area' &&
58514                                 osmRoutableHighwayTagValues[parentWay.tags.highway]) return true;
58515
58516                             // count connections to ferry routes as connected
58517                             if (parentWay.tags.route === 'ferry') return true;
58518
58519                             return graph.parentRelations(parentWay).some(function(parentRelation) {
58520                                 if (parentRelation.tags.type === 'route' &&
58521                                     parentRelation.tags.route === 'ferry') return true;
58522
58523                                 // allow connections to highway multipolygons
58524                                 return parentRelation.isMultipolygon() && osmRoutableHighwayTagValues[parentRelation.tags.highway];
58525                             });
58526                         } else if (wayType === 'waterway') {
58527                             // multiple waterways may start or end at a water body at the same node
58528                             if (parentWay.tags.natural === 'water' ||
58529                                 parentWay.tags.natural === 'coastline') return true;
58530                         }
58531                         return false;
58532                     });
58533                 }
58534
58535                 function issuesForNode(way, nodeID) {
58536
58537                     var isFirst = nodeID === way.first();
58538
58539                     var wayType = typeForWay(way);
58540
58541                     // ignore if this way is self-connected at this node
58542                     if (nodeOccursMoreThanOnce(way, nodeID)) return [];
58543
58544                     var osm = services.osm;
58545                     if (!osm) return [];
58546
58547                     var node = graph.hasEntity(nodeID);
58548
58549                     // ignore if this node or its tile are unloaded
58550                     if (!node || !osm.isDataLoaded(node.loc)) return [];
58551
58552                     if (isConnectedViaOtherTypes(way, node)) return [];
58553
58554                     var attachedWaysOfSameType = graph.parentWays(node).filter(function(parentWay) {
58555                         if (parentWay.id === way.id) return false;
58556                         return typeForWay(parentWay) === wayType;
58557                     });
58558
58559                     // assume it's okay for waterways to start or end disconnected for now
58560                     if (wayType === 'waterway' && attachedWaysOfSameType.length === 0) return [];
58561
58562                     var attachedOneways = attachedWaysOfSameType.filter(function(attachedWay) {
58563                         return isOneway(attachedWay);
58564                     });
58565
58566                     // ignore if the way is connected to some non-oneway features
58567                     if (attachedOneways.length < attachedWaysOfSameType.length) return [];
58568
58569                     if (attachedOneways.length) {
58570                         var connectedEndpointsOkay = attachedOneways.some(function(attachedOneway) {
58571                             if ((isFirst ? attachedOneway.first() : attachedOneway.last()) !== nodeID) return true;
58572                             if (nodeOccursMoreThanOnce(attachedOneway, nodeID)) return true;
58573                             return false;
58574                         });
58575                         if (connectedEndpointsOkay) return [];
58576                     }
58577
58578                     var placement = isFirst ? 'start' : 'end',
58579                         messageID = wayType + '.',
58580                         referenceID = wayType + '.';
58581
58582                     if (wayType === 'waterway') {
58583                         messageID += 'connected.' + placement;
58584                         referenceID += 'connected';
58585                     } else {
58586                         messageID += placement;
58587                         referenceID += placement;
58588                     }
58589
58590                     return [new validationIssue({
58591                         type: type,
58592                         subtype: wayType,
58593                         severity: 'warning',
58594                         message: function(context) {
58595                             var entity = context.hasEntity(this.entityIds[0]);
58596                             return entity ? _t('issues.impossible_oneway.' + messageID + '.message', {
58597                                 feature: utilDisplayLabel(entity, context.graph())
58598                             }) : '';
58599                         },
58600                         reference: getReference(referenceID),
58601                         entityIds: [way.id, node.id],
58602                         dynamicFixes: function() {
58603
58604                             var fixes = [];
58605
58606                             if (attachedOneways.length) {
58607                                 fixes.push(new validationIssueFix({
58608                                     icon: 'iD-operation-reverse',
58609                                     title: _t('issues.fix.reverse_feature.title'),
58610                                     entityIds: [way.id],
58611                                     onClick: function(context) {
58612                                         var id = this.issue.entityIds[0];
58613                                         context.perform(actionReverse(id), _t('operations.reverse.annotation'));
58614                                     }
58615                                 }));
58616                             }
58617                             if (node.tags.noexit !== 'yes') {
58618                                 var textDirection = _mainLocalizer.textDirection();
58619                                 var useLeftContinue = (isFirst && textDirection === 'ltr') ||
58620                                     (!isFirst && textDirection === 'rtl');
58621                                 fixes.push(new validationIssueFix({
58622                                     icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
58623                                     title: _t('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'),
58624                                     onClick: function(context) {
58625                                         var entityID = this.issue.entityIds[0];
58626                                         var vertexID = this.issue.entityIds[1];
58627                                         var way = context.entity(entityID);
58628                                         var vertex = context.entity(vertexID);
58629                                         continueDrawing(way, vertex, context);
58630                                     }
58631                                 }));
58632                             }
58633
58634                             return fixes;
58635                         },
58636                         loc: node.loc
58637                     })];
58638
58639                     function getReference(referenceID) {
58640                         return function showReference(selection) {
58641                             selection.selectAll('.issue-reference')
58642                                 .data([0])
58643                                 .enter()
58644                                 .append('div')
58645                                 .attr('class', 'issue-reference')
58646                                 .text(_t('issues.impossible_oneway.' + referenceID + '.reference'));
58647                         };
58648                     }
58649                 }
58650             };
58651
58652             function continueDrawing(way, vertex, context) {
58653                 // make sure the vertex is actually visible and editable
58654                 var map = context.map();
58655                 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
58656                     map.zoomToEase(vertex);
58657                 }
58658
58659                 context.enter(
58660                     modeDrawLine(context, way.id, context.graph(), 'line', way.affix(vertex.id), true)
58661                 );
58662             }
58663
58664             validation.type = type;
58665
58666             return validation;
58667         }
58668
58669         function validationIncompatibleSource() {
58670             var type = 'incompatible_source';
58671             var invalidSources = [
58672                 {
58673                     id:'google', regex:'google', exceptRegex: 'books.google|Google Books|drive.google|googledrive|Google Drive'
58674                 }
58675             ];
58676
58677             var validation = function checkIncompatibleSource(entity) {
58678
58679                 var entitySources = entity.tags && entity.tags.source && entity.tags.source.split(';');
58680
58681                 if (!entitySources) return [];
58682
58683                 var issues = [];
58684
58685                 invalidSources.forEach(function(invalidSource) {
58686
58687                     var hasInvalidSource = entitySources.some(function(source) {
58688                         if (!source.match(new RegExp(invalidSource.regex, 'i'))) return false;
58689                         if (invalidSource.exceptRegex && source.match(new RegExp(invalidSource.exceptRegex, 'i'))) return false;
58690                         return true;
58691                     });
58692
58693                     if (!hasInvalidSource) return;
58694
58695                     issues.push(new validationIssue({
58696                         type: type,
58697                         severity: 'warning',
58698                         message: function(context) {
58699                             var entity = context.hasEntity(this.entityIds[0]);
58700                             return entity ? _t('issues.incompatible_source.' + invalidSource.id + '.feature.message', {
58701                                 feature: utilDisplayLabel(entity, context.graph())
58702                             }) : '';
58703                         },
58704                         reference: getReference(invalidSource.id),
58705                         entityIds: [entity.id],
58706                         dynamicFixes: function() {
58707                             return [
58708                                 new validationIssueFix({
58709                                     title: _t('issues.fix.remove_proprietary_data.title')
58710                                 })
58711                             ];
58712                         }
58713                     }));
58714                 });
58715
58716                 return issues;
58717
58718
58719                 function getReference(id) {
58720                     return function showReference(selection) {
58721                         selection.selectAll('.issue-reference')
58722                             .data([0])
58723                             .enter()
58724                             .append('div')
58725                             .attr('class', 'issue-reference')
58726                             .text(_t('issues.incompatible_source.' + id + '.reference'));
58727                     };
58728                 }
58729             };
58730
58731             validation.type = type;
58732
58733             return validation;
58734         }
58735
58736         function validationMaprules() {
58737             var type = 'maprules';
58738
58739             var validation = function checkMaprules(entity, graph) {
58740                 if (!services.maprules) return [];
58741
58742                 var rules = services.maprules.validationRules();
58743                 var issues = [];
58744
58745                 for (var i = 0; i < rules.length; i++) {
58746                     var rule = rules[i];
58747                     rule.findIssues(entity, graph, issues);
58748                 }
58749
58750                 return issues;
58751             };
58752
58753
58754             validation.type = type;
58755
58756             return validation;
58757         }
58758
58759         function validationMismatchedGeometry() {
58760             var type = 'mismatched_geometry';
58761
58762             function tagSuggestingLineIsArea(entity) {
58763                 if (entity.type !== 'way' || entity.isClosed()) return null;
58764
58765                 var tagSuggestingArea = entity.tagSuggestingArea();
58766                 if (!tagSuggestingArea) {
58767                     return null;
58768                 }
58769
58770                 var asLine = _mainPresetIndex.matchTags(tagSuggestingArea, 'line');
58771                 var asArea = _mainPresetIndex.matchTags(tagSuggestingArea, 'area');
58772                 if (asLine && asArea && (asLine === asArea)) {
58773                     // these tags also allow lines and making this an area wouldn't matter
58774                     return null;
58775                 }
58776
58777                 return tagSuggestingArea;
58778             }
58779
58780
58781             function makeConnectEndpointsFixOnClick(way, graph) {
58782                 // must have at least three nodes to close this automatically
58783                 if (way.nodes.length < 3) return null;
58784
58785                 var nodes = graph.childNodes(way), testNodes;
58786                 var firstToLastDistanceMeters = geoSphericalDistance(nodes[0].loc, nodes[nodes.length-1].loc);
58787
58788                 // if the distance is very small, attempt to merge the endpoints
58789                 if (firstToLastDistanceMeters < 0.75) {
58790                     testNodes = nodes.slice();   // shallow copy
58791                     testNodes.pop();
58792                     testNodes.push(testNodes[0]);
58793                     // make sure this will not create a self-intersection
58794                     if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
58795                         return function(context) {
58796                             var way = context.entity(this.issue.entityIds[0]);
58797                             context.perform(
58798                                 actionMergeNodes([way.nodes[0], way.nodes[way.nodes.length-1]], nodes[0].loc),
58799                                 _t('issues.fix.connect_endpoints.annotation')
58800                             );
58801                         };
58802                     }
58803                 }
58804
58805                 // if the points were not merged, attempt to close the way
58806                 testNodes = nodes.slice();   // shallow copy
58807                 testNodes.push(testNodes[0]);
58808                 // make sure this will not create a self-intersection
58809                 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
58810                     return function(context) {
58811                         var wayId = this.issue.entityIds[0];
58812                         var way = context.entity(wayId);
58813                         var nodeId = way.nodes[0];
58814                         var index = way.nodes.length;
58815                         context.perform(
58816                             actionAddVertex(wayId, nodeId, index),
58817                             _t('issues.fix.connect_endpoints.annotation')
58818                         );
58819                     };
58820                 }
58821             }
58822
58823             function lineTaggedAsAreaIssue(entity) {
58824
58825                 var tagSuggestingArea = tagSuggestingLineIsArea(entity);
58826                 if (!tagSuggestingArea) return null;
58827
58828                 return new validationIssue({
58829                     type: type,
58830                     subtype: 'area_as_line',
58831                     severity: 'warning',
58832                     message: function(context) {
58833                         var entity = context.hasEntity(this.entityIds[0]);
58834                         return entity ? _t('issues.tag_suggests_area.message', {
58835                             feature: utilDisplayLabel(entity, context.graph()),
58836                             tag: utilTagText({ tags: tagSuggestingArea })
58837                         }) : '';
58838                     },
58839                     reference: showReference,
58840                     entityIds: [entity.id],
58841                     hash: JSON.stringify(tagSuggestingArea),
58842                     dynamicFixes: function(context) {
58843
58844                         var fixes = [];
58845
58846                         var entity = context.entity(this.entityIds[0]);
58847                         var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph());
58848
58849                         fixes.push(new validationIssueFix({
58850                             title: _t('issues.fix.connect_endpoints.title'),
58851                             onClick: connectEndsOnClick
58852                         }));
58853
58854                         fixes.push(new validationIssueFix({
58855                             icon: 'iD-operation-delete',
58856                             title: _t('issues.fix.remove_tag.title'),
58857                             onClick: function(context) {
58858                                 var entityId = this.issue.entityIds[0];
58859                                 var entity = context.entity(entityId);
58860                                 var tags = Object.assign({}, entity.tags);  // shallow copy
58861                                 for (var key in tagSuggestingArea) {
58862                                     delete tags[key];
58863                                 }
58864                                 context.perform(
58865                                     actionChangeTags(entityId, tags),
58866                                     _t('issues.fix.remove_tag.annotation')
58867                                 );
58868                             }
58869                         }));
58870
58871                         return fixes;
58872                     }
58873                 });
58874
58875
58876                 function showReference(selection) {
58877                     selection.selectAll('.issue-reference')
58878                         .data([0])
58879                         .enter()
58880                         .append('div')
58881                         .attr('class', 'issue-reference')
58882                         .text(_t('issues.tag_suggests_area.reference'));
58883                 }
58884             }
58885
58886             function vertexTaggedAsPointIssue(entity, graph) {
58887                 // we only care about nodes
58888                 if (entity.type !== 'node') return null;
58889
58890                 // ignore tagless points
58891                 if (Object.keys(entity.tags).length === 0) return null;
58892
58893                 // address lines are special so just ignore them
58894                 if (entity.isOnAddressLine(graph)) return null;
58895
58896                 var geometry = entity.geometry(graph);
58897                 var allowedGeometries = osmNodeGeometriesForTags(entity.tags);
58898
58899                 if (geometry === 'point' && !allowedGeometries.point && allowedGeometries.vertex) {
58900
58901                     return new validationIssue({
58902                         type: type,
58903                         subtype: 'vertex_as_point',
58904                         severity: 'warning',
58905                         message: function(context) {
58906                             var entity = context.hasEntity(this.entityIds[0]);
58907                             return entity ? _t('issues.vertex_as_point.message', {
58908                                 feature: utilDisplayLabel(entity, context.graph())
58909                             }) : '';
58910                         },
58911                         reference: function showReference(selection) {
58912                             selection.selectAll('.issue-reference')
58913                                 .data([0])
58914                                 .enter()
58915                                 .append('div')
58916                                 .attr('class', 'issue-reference')
58917                                 .text(_t('issues.vertex_as_point.reference'));
58918                         },
58919                         entityIds: [entity.id]
58920                     });
58921
58922                 } else if (geometry === 'vertex' && !allowedGeometries.vertex && allowedGeometries.point) {
58923
58924                     return new validationIssue({
58925                         type: type,
58926                         subtype: 'point_as_vertex',
58927                         severity: 'warning',
58928                         message: function(context) {
58929                             var entity = context.hasEntity(this.entityIds[0]);
58930                             return entity ? _t('issues.point_as_vertex.message', {
58931                                 feature: utilDisplayLabel(entity, context.graph())
58932                             }) : '';
58933                         },
58934                         reference: function showReference(selection) {
58935                             selection.selectAll('.issue-reference')
58936                                 .data([0])
58937                                 .enter()
58938                                 .append('div')
58939                                 .attr('class', 'issue-reference')
58940                                 .text(_t('issues.point_as_vertex.reference'));
58941                         },
58942                         entityIds: [entity.id],
58943                         dynamicFixes: function(context) {
58944
58945                             var entityId = this.entityIds[0];
58946
58947                             var extractOnClick = null;
58948                             if (!context.hasHiddenConnections(entityId)) {
58949
58950                                 extractOnClick = function(context) {
58951                                     var entityId = this.issue.entityIds[0];
58952                                     var action = actionExtract(entityId);
58953                                     context.perform(
58954                                         action,
58955                                         _t('operations.extract.annotation.single')
58956                                     );
58957                                     // re-enter mode to trigger updates
58958                                     context.enter(modeSelect(context, [action.getExtractedNodeID()]));
58959                                 };
58960                             }
58961
58962                             return [
58963                                 new validationIssueFix({
58964                                     icon: 'iD-operation-extract',
58965                                     title: _t('issues.fix.extract_point.title'),
58966                                     onClick: extractOnClick
58967                                 })
58968                             ];
58969                         }
58970                     });
58971                 }
58972
58973                 return null;
58974             }
58975
58976             function unclosedMultipolygonPartIssues(entity, graph) {
58977
58978                 if (entity.type !== 'relation' ||
58979                     !entity.isMultipolygon() ||
58980                     entity.isDegenerate() ||
58981                     // cannot determine issues for incompletely-downloaded relations
58982                     !entity.isComplete(graph)) return null;
58983
58984                 var sequences = osmJoinWays(entity.members, graph);
58985
58986                 var issues = [];
58987
58988                 for (var i in sequences) {
58989                     var sequence = sequences[i];
58990
58991                     if (!sequence.nodes) continue;
58992
58993                     var firstNode = sequence.nodes[0];
58994                     var lastNode = sequence.nodes[sequence.nodes.length - 1];
58995
58996                     // part is closed if the first and last nodes are the same
58997                     if (firstNode === lastNode) continue;
58998
58999                     var issue = new validationIssue({
59000                         type: type,
59001                         subtype: 'unclosed_multipolygon_part',
59002                         severity: 'warning',
59003                         message: function(context) {
59004                             var entity = context.hasEntity(this.entityIds[0]);
59005                             return entity ? _t('issues.unclosed_multipolygon_part.message', {
59006                                 feature: utilDisplayLabel(entity, context.graph())
59007                             }) : '';
59008                         },
59009                         reference: showReference,
59010                         loc: sequence.nodes[0].loc,
59011                         entityIds: [entity.id],
59012                         hash: sequence.map(function(way) {
59013                             return way.id;
59014                         }).join()
59015                     });
59016                     issues.push(issue);
59017                 }
59018
59019                 return issues;
59020
59021                 function showReference(selection) {
59022                     selection.selectAll('.issue-reference')
59023                         .data([0])
59024                         .enter()
59025                         .append('div')
59026                         .attr('class', 'issue-reference')
59027                         .text(_t('issues.unclosed_multipolygon_part.reference'));
59028                 }
59029             }
59030
59031             var validation = function checkMismatchedGeometry(entity, graph) {
59032                 var issues = [
59033                     vertexTaggedAsPointIssue(entity, graph),
59034                     lineTaggedAsAreaIssue(entity)
59035                 ];
59036                 issues = issues.concat(unclosedMultipolygonPartIssues(entity, graph));
59037                 return issues.filter(Boolean);
59038             };
59039
59040             validation.type = type;
59041
59042             return validation;
59043         }
59044
59045         function validationMissingRole() {
59046             var type = 'missing_role';
59047
59048             var validation = function checkMissingRole(entity, graph) {
59049                 var issues = [];
59050                 if (entity.type === 'way') {
59051                     graph.parentRelations(entity).forEach(function(relation) {
59052                         if (!relation.isMultipolygon()) return;
59053
59054                         var member = relation.memberById(entity.id);
59055                         if (member && isMissingRole(member)) {
59056                             issues.push(makeIssue(entity, relation, member));
59057                         }
59058                     });
59059                 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
59060                     entity.indexedMembers().forEach(function(member) {
59061                         var way = graph.hasEntity(member.id);
59062                         if (way && isMissingRole(member)) {
59063                             issues.push(makeIssue(way, entity, member));
59064                         }
59065                     });
59066                 }
59067
59068                 return issues;
59069             };
59070
59071
59072             function isMissingRole(member) {
59073                 return !member.role || !member.role.trim().length;
59074             }
59075
59076
59077             function makeIssue(way, relation, member) {
59078                 return new validationIssue({
59079                     type: type,
59080                     severity: 'warning',
59081                     message: function(context) {
59082                         var member = context.hasEntity(this.entityIds[1]),
59083                             relation = context.hasEntity(this.entityIds[0]);
59084                         return (member && relation) ? _t('issues.missing_role.message', {
59085                             member: utilDisplayLabel(member, context.graph()),
59086                             relation: utilDisplayLabel(relation, context.graph())
59087                         }) : '';
59088                     },
59089                     reference: showReference,
59090                     entityIds: [relation.id, way.id],
59091                     data: {
59092                         member: member
59093                     },
59094                     hash: member.index.toString(),
59095                     dynamicFixes: function() {
59096                         return [
59097                             makeAddRoleFix('inner'),
59098                             makeAddRoleFix('outer'),
59099                             new validationIssueFix({
59100                                 icon: 'iD-operation-delete',
59101                                 title: _t('issues.fix.remove_from_relation.title'),
59102                                 onClick: function(context) {
59103                                     context.perform(
59104                                         actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index),
59105                                         _t('operations.delete_member.annotation')
59106                                     );
59107                                 }
59108                             })
59109                         ];
59110                     }
59111                 });
59112
59113
59114                 function showReference(selection) {
59115                     selection.selectAll('.issue-reference')
59116                         .data([0])
59117                         .enter()
59118                         .append('div')
59119                         .attr('class', 'issue-reference')
59120                         .text(_t('issues.missing_role.multipolygon.reference'));
59121                 }
59122             }
59123
59124
59125             function makeAddRoleFix(role) {
59126                 return new validationIssueFix({
59127                     title: _t('issues.fix.set_as_' + role + '.title'),
59128                     onClick: function(context) {
59129                         var oldMember = this.issue.data.member;
59130                         var member = { id: this.issue.entityIds[1], type: oldMember.type, role: role };
59131                         context.perform(
59132                             actionChangeMember(this.issue.entityIds[0], member, oldMember.index),
59133                             _t('operations.change_role.annotation')
59134                         );
59135                     }
59136                 });
59137             }
59138
59139             validation.type = type;
59140
59141             return validation;
59142         }
59143
59144         function validationMissingTag(context) {
59145             var type = 'missing_tag';
59146
59147             function hasDescriptiveTags(entity, graph) {
59148                 var keys = Object.keys(entity.tags)
59149                     .filter(function(k) {
59150                         if (k === 'area' || k === 'name') {
59151                             return false;
59152                         } else {
59153                             return osmIsInterestingTag(k);
59154                         }
59155                     });
59156
59157                 if (entity.type === 'relation' &&
59158                     keys.length === 1 &&
59159                     entity.tags.type === 'multipolygon') {
59160                     // this relation's only interesting tag just says its a multipolygon,
59161                     // which is not descriptive enough
59162
59163                     // It's okay for a simple multipolygon to have no descriptive tags
59164                     // if its outer way has them (old model, see `outdated_tags.js`)
59165                     return osmOldMultipolygonOuterMemberOfRelation(entity, graph);
59166                 }
59167
59168                 return keys.length > 0;
59169             }
59170
59171             function isUnknownRoad(entity) {
59172                 return entity.type === 'way' && entity.tags.highway === 'road';
59173             }
59174
59175             function isUntypedRelation(entity) {
59176                 return entity.type === 'relation' && !entity.tags.type;
59177             }
59178
59179             var validation = function checkMissingTag(entity, graph) {
59180
59181                 var subtype;
59182
59183                 var osm = context.connection();
59184                 var isUnloadedNode = entity.type === 'node' && osm && !osm.isDataLoaded(entity.loc);
59185
59186                 // we can't know if the node is a vertex if the tile is undownloaded
59187                 if (!isUnloadedNode &&
59188                     // allow untagged nodes that are part of ways
59189                     entity.geometry(graph) !== 'vertex' &&
59190                     // allow untagged entities that are part of relations
59191                     !entity.hasParentRelations(graph)) {
59192
59193                     if (Object.keys(entity.tags).length === 0) {
59194                         subtype = 'any';
59195                     } else if (!hasDescriptiveTags(entity, graph)) {
59196                         subtype = 'descriptive';
59197                     } else if (isUntypedRelation(entity)) {
59198                         subtype = 'relation_type';
59199                     }
59200                 }
59201
59202                 // flag an unknown road even if it's a member of a relation
59203                 if (!subtype && isUnknownRoad(entity)) {
59204                     subtype = 'highway_classification';
59205                 }
59206
59207                 if (!subtype) return [];
59208
59209                 var messageID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag.' + subtype;
59210                 var referenceID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag';
59211
59212                 // can always delete if the user created it in the first place..
59213                 var canDelete = (entity.version === undefined || entity.v !== undefined);
59214                 var severity = (canDelete && subtype !== 'highway_classification') ? 'error' : 'warning';
59215
59216                 return [new validationIssue({
59217                     type: type,
59218                     subtype: subtype,
59219                     severity: severity,
59220                     message: function(context) {
59221                         var entity = context.hasEntity(this.entityIds[0]);
59222                         return entity ? _t('issues.' + messageID + '.message', {
59223                             feature: utilDisplayLabel(entity, context.graph())
59224                         }) : '';
59225                     },
59226                     reference: showReference,
59227                     entityIds: [entity.id],
59228                     dynamicFixes: function(context) {
59229
59230                         var fixes = [];
59231
59232                         var selectFixType = subtype === 'highway_classification' ? 'select_road_type' : 'select_preset';
59233
59234                         fixes.push(new validationIssueFix({
59235                             icon: 'iD-icon-search',
59236                             title: _t('issues.fix.' + selectFixType + '.title'),
59237                             onClick: function(context) {
59238                                 context.ui().sidebar.showPresetList();
59239                             }
59240                         }));
59241
59242                         var deleteOnClick;
59243
59244                         var id = this.entityIds[0];
59245                         var operation = operationDelete(context, [id]);
59246                         var disabledReasonID = operation.disabled();
59247                         if (!disabledReasonID) {
59248                             deleteOnClick = function(context) {
59249                                 var id = this.issue.entityIds[0];
59250                                 var operation = operationDelete(context, [id]);
59251                                 if (!operation.disabled()) {
59252                                     operation();
59253                                 }
59254                             };
59255                         }
59256
59257                         fixes.push(
59258                             new validationIssueFix({
59259                                 icon: 'iD-operation-delete',
59260                                 title: _t('issues.fix.delete_feature.title'),
59261                                 disabledReason: disabledReasonID ? _t('operations.delete.' + disabledReasonID + '.single') : undefined,
59262                                 onClick: deleteOnClick
59263                             })
59264                         );
59265
59266                         return fixes;
59267                     }
59268                 })];
59269
59270                 function showReference(selection) {
59271                     selection.selectAll('.issue-reference')
59272                         .data([0])
59273                         .enter()
59274                         .append('div')
59275                         .attr('class', 'issue-reference')
59276                         .text(_t('issues.' + referenceID + '.reference'));
59277                 }
59278             };
59279
59280             validation.type = type;
59281
59282             return validation;
59283         }
59284
59285         // remove spaces, punctuation, diacritics
59286         var simplify = (str) => {
59287           return diacritics.remove(
59288             str
59289               .replace(/&/g, 'and')
59290               .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,'')
59291               .toLowerCase()
59292           );
59293         };
59294
59295         // toParts - split a name-suggestion-index key into parts
59296         // {
59297         //   kvnd:        "amenity/fast_food|Thaï Express~(North America)",
59298         //   kvn:         "amenity/fast_food|Thaï Express",
59299         //   kv:          "amenity/fast_food",
59300         //   k:           "amenity",
59301         //   v:           "fast_food",
59302         //   n:           "Thaï Express",
59303         //   d:           "(North America)",
59304         //   nsimple:     "thaiexpress",
59305         //   kvnnsimple:  "amenity/fast_food|thaiexpress"
59306         // }
59307         var to_parts = (kvnd) => {
59308           const parts = {};
59309           parts.kvnd = kvnd;
59310
59311           const kvndparts = kvnd.split('~', 2);
59312           if (kvndparts.length > 1) parts.d = kvndparts[1];
59313
59314           parts.kvn = kvndparts[0];
59315           const kvnparts = parts.kvn.split('|', 2);
59316           if (kvnparts.length > 1) parts.n = kvnparts[1];
59317
59318           parts.kv = kvnparts[0];
59319           const kvparts = parts.kv.split('/', 2);
59320           parts.k = kvparts[0];
59321           parts.v = kvparts[1];
59322
59323           parts.nsimple = simplify(parts.n);
59324           parts.kvnsimple = parts.kv + '|' + parts.nsimple;
59325           return parts;
59326         };
59327
59328         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"]};
59329         var match_groups = {
59330         matchGroups: matchGroups
59331         };
59332
59333         var match_groups$1 = /*#__PURE__*/Object.freeze({
59334                 __proto__: null,
59335                 matchGroups: matchGroups,
59336                 'default': match_groups
59337         });
59338
59339         var require$$0 = getCjsExportFromNamespace(match_groups$1);
59340
59341         const matchGroups$1 = require$$0.matchGroups;
59342
59343
59344         var matcher$1 = () => {
59345           let _warnings = [];  // array of match conflict pairs
59346           let _ambiguous = {};
59347           let _matchIndex = {};
59348           let matcher = {};
59349
59350
59351           // Create an index of all the keys/simplenames for fast matching
59352           matcher.buildMatchIndex = (brands) => {
59353             // two passes - once for primary names, once for secondary/alternate names
59354             Object.keys(brands).forEach(kvnd => insertNames(kvnd, 'primary'));
59355             Object.keys(brands).forEach(kvnd => insertNames(kvnd, 'secondary'));
59356
59357
59358             function insertNames(kvnd, which) {
59359               const obj = brands[kvnd];
59360               const parts = to_parts(kvnd);
59361
59362               // Exit early for ambiguous names in the second pass.
59363               // They were collected in the first pass and we don't gather alt names for them.
59364               if (which === 'secondary' && parts.d) return;
59365
59366
59367               if (obj.countryCodes) {
59368                 parts.countryCodes = obj.countryCodes.slice();  // copy
59369               }
59370
59371               const nomatches = (obj.nomatch || []);
59372               if (nomatches.some(s => s === kvnd)) {
59373                 console.log(`WARNING match/nomatch conflict for ${kvnd}`);
59374                 return;
59375               }
59376
59377               const match_kv = [parts.kv]
59378                 .concat(obj.matchTags || [])
59379                 .concat([`${parts.k}/yes`, `building/yes`])   // #3454 - match some generic tags
59380                 .map(s => s.toLowerCase());
59381
59382               let match_nsimple = [];
59383               if (which === 'primary') {
59384                 match_nsimple = [parts.n]
59385                   .concat(obj.matchNames || [])
59386                   .concat(obj.tags.official_name || [])   // #2732 - match alternate names
59387                   .map(simplify);
59388
59389               } else if (which === 'secondary') {
59390                 match_nsimple = []
59391                   .concat(obj.tags.alt_name || [])        // #2732 - match alternate names
59392                   .concat(obj.tags.short_name || [])      // #2732 - match alternate names
59393                   .map(simplify);
59394               }
59395
59396               if (!match_nsimple.length) return;  // nothing to do
59397
59398               match_kv.forEach(kv => {
59399                 match_nsimple.forEach(nsimple => {
59400                   if (parts.d) {
59401                     // Known ambiguous names with disambiguation string ~(USA) / ~(Canada)
59402                     // FIXME: Name collisions will overwrite the initial entry (ok for now)
59403                     if (!_ambiguous[kv]) _ambiguous[kv] = {};
59404                     _ambiguous[kv][nsimple] = parts;
59405
59406                   } else {
59407                     // Names we mostly expect to be unique..
59408                     if (!_matchIndex[kv]) _matchIndex[kv] = {};
59409
59410                     const m = _matchIndex[kv][nsimple];
59411                     if (m) {  // There already is a match for this name, skip it
59412                       // Warn if we detect collisions in a primary name.
59413                       // Skip warning if a secondary name or a generic `*=yes` tag - #2972 / #3454
59414                       if (which === 'primary' && !/\/yes$/.test(kv)) {
59415                         _warnings.push([m.kvnd, `${kvnd} (${kv}/${nsimple})`]);
59416                       }
59417                     } else {
59418                       _matchIndex[kv][nsimple] = parts;   // insert
59419                     }
59420                   }
59421                 });
59422               });
59423
59424             }
59425           };
59426
59427
59428           // pass a `key`, `value`, `name` and return the best match,
59429           // `countryCode` optional (if supplied, must match that too)
59430           matcher.matchKVN = (key, value, name, countryCode) => {
59431             return matcher.matchParts(to_parts(`${key}/${value}|${name}`), countryCode);
59432           };
59433
59434
59435           // pass a parts object and return the best match,
59436           // `countryCode` optional (if supplied, must match that too)
59437           matcher.matchParts = (parts, countryCode) => {
59438             let match = null;
59439             let inGroup = false;
59440
59441             // fixme: we currently return a single match for ambiguous
59442             match = _ambiguous[parts.kv] && _ambiguous[parts.kv][parts.nsimple];
59443             if (match && matchesCountryCode(match)) return match;
59444
59445             // try to return an exact match
59446             match = _matchIndex[parts.kv] && _matchIndex[parts.kv][parts.nsimple];
59447             if (match && matchesCountryCode(match)) return match;
59448
59449             // look in match groups
59450             for (let mg in matchGroups$1) {
59451               const matchGroup = matchGroups$1[mg];
59452               match = null;
59453               inGroup = false;
59454
59455               for (let i = 0; i < matchGroup.length; i++) {
59456                 const otherkv = matchGroup[i].toLowerCase();
59457                 if (!inGroup) {
59458                   inGroup = otherkv === parts.kv;
59459                 }
59460                 if (!match) {
59461                   // fixme: we currently return a single match for ambiguous
59462                   match = _ambiguous[otherkv] && _ambiguous[otherkv][parts.nsimple];
59463                 }
59464                 if (!match) {
59465                   match = _matchIndex[otherkv] && _matchIndex[otherkv][parts.nsimple];
59466                 }
59467
59468                 if (match && !matchesCountryCode(match)) {
59469                   match = null;
59470                 }
59471
59472                 if (inGroup && match) {
59473                   return match;
59474                 }
59475               }
59476             }
59477
59478             return null;
59479
59480             function matchesCountryCode(match) {
59481               if (!countryCode) return true;
59482               if (!match.countryCodes) return true;
59483               return match.countryCodes.indexOf(countryCode) !== -1;
59484             }
59485           };
59486
59487
59488           matcher.getWarnings = () => _warnings;
59489
59490           return matcher;
59491         };
59492
59493         var quickselect$1 = createCommonjsModule(function (module, exports) {
59494         (function (global, factory) {
59495                  module.exports = factory() ;
59496         }(commonjsGlobal, (function () {
59497         function quickselect(arr, k, left, right, compare) {
59498             quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
59499         }
59500
59501         function quickselectStep(arr, k, left, right, compare) {
59502
59503             while (right > left) {
59504                 if (right - left > 600) {
59505                     var n = right - left + 1;
59506                     var m = k - left + 1;
59507                     var z = Math.log(n);
59508                     var s = 0.5 * Math.exp(2 * z / 3);
59509                     var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
59510                     var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
59511                     var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
59512                     quickselectStep(arr, k, newLeft, newRight, compare);
59513                 }
59514
59515                 var t = arr[k];
59516                 var i = left;
59517                 var j = right;
59518
59519                 swap(arr, left, k);
59520                 if (compare(arr[right], t) > 0) swap(arr, left, right);
59521
59522                 while (i < j) {
59523                     swap(arr, i, j);
59524                     i++;
59525                     j--;
59526                     while (compare(arr[i], t) < 0) i++;
59527                     while (compare(arr[j], t) > 0) j--;
59528                 }
59529
59530                 if (compare(arr[left], t) === 0) swap(arr, left, j);
59531                 else {
59532                     j++;
59533                     swap(arr, j, right);
59534                 }
59535
59536                 if (j <= k) left = j + 1;
59537                 if (k <= j) right = j - 1;
59538             }
59539         }
59540
59541         function swap(arr, i, j) {
59542             var tmp = arr[i];
59543             arr[i] = arr[j];
59544             arr[j] = tmp;
59545         }
59546
59547         function defaultCompare(a, b) {
59548             return a < b ? -1 : a > b ? 1 : 0;
59549         }
59550
59551         return quickselect;
59552
59553         })));
59554         });
59555
59556         var rbush_1 = rbush;
59557         var _default$2 = rbush;
59558
59559
59560
59561         function rbush(maxEntries, format) {
59562             if (!(this instanceof rbush)) return new rbush(maxEntries, format);
59563
59564             // max entries in a node is 9 by default; min node fill is 40% for best performance
59565             this._maxEntries = Math.max(4, maxEntries || 9);
59566             this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
59567
59568             if (format) {
59569                 this._initFormat(format);
59570             }
59571
59572             this.clear();
59573         }
59574
59575         rbush.prototype = {
59576
59577             all: function () {
59578                 return this._all(this.data, []);
59579             },
59580
59581             search: function (bbox) {
59582
59583                 var node = this.data,
59584                     result = [],
59585                     toBBox = this.toBBox;
59586
59587                 if (!intersects$1(bbox, node)) return result;
59588
59589                 var nodesToSearch = [],
59590                     i, len, child, childBBox;
59591
59592                 while (node) {
59593                     for (i = 0, len = node.children.length; i < len; i++) {
59594
59595                         child = node.children[i];
59596                         childBBox = node.leaf ? toBBox(child) : child;
59597
59598                         if (intersects$1(bbox, childBBox)) {
59599                             if (node.leaf) result.push(child);
59600                             else if (contains$2(bbox, childBBox)) this._all(child, result);
59601                             else nodesToSearch.push(child);
59602                         }
59603                     }
59604                     node = nodesToSearch.pop();
59605                 }
59606
59607                 return result;
59608             },
59609
59610             collides: function (bbox) {
59611
59612                 var node = this.data,
59613                     toBBox = this.toBBox;
59614
59615                 if (!intersects$1(bbox, node)) return false;
59616
59617                 var nodesToSearch = [],
59618                     i, len, child, childBBox;
59619
59620                 while (node) {
59621                     for (i = 0, len = node.children.length; i < len; i++) {
59622
59623                         child = node.children[i];
59624                         childBBox = node.leaf ? toBBox(child) : child;
59625
59626                         if (intersects$1(bbox, childBBox)) {
59627                             if (node.leaf || contains$2(bbox, childBBox)) return true;
59628                             nodesToSearch.push(child);
59629                         }
59630                     }
59631                     node = nodesToSearch.pop();
59632                 }
59633
59634                 return false;
59635             },
59636
59637             load: function (data) {
59638                 if (!(data && data.length)) return this;
59639
59640                 if (data.length < this._minEntries) {
59641                     for (var i = 0, len = data.length; i < len; i++) {
59642                         this.insert(data[i]);
59643                     }
59644                     return this;
59645                 }
59646
59647                 // recursively build the tree with the given data from scratch using OMT algorithm
59648                 var node = this._build(data.slice(), 0, data.length - 1, 0);
59649
59650                 if (!this.data.children.length) {
59651                     // save as is if tree is empty
59652                     this.data = node;
59653
59654                 } else if (this.data.height === node.height) {
59655                     // split root if trees have the same height
59656                     this._splitRoot(this.data, node);
59657
59658                 } else {
59659                     if (this.data.height < node.height) {
59660                         // swap trees if inserted one is bigger
59661                         var tmpNode = this.data;
59662                         this.data = node;
59663                         node = tmpNode;
59664                     }
59665
59666                     // insert the small tree into the large tree at appropriate level
59667                     this._insert(node, this.data.height - node.height - 1, true);
59668                 }
59669
59670                 return this;
59671             },
59672
59673             insert: function (item) {
59674                 if (item) this._insert(item, this.data.height - 1);
59675                 return this;
59676             },
59677
59678             clear: function () {
59679                 this.data = createNode$1([]);
59680                 return this;
59681             },
59682
59683             remove: function (item, equalsFn) {
59684                 if (!item) return this;
59685
59686                 var node = this.data,
59687                     bbox = this.toBBox(item),
59688                     path = [],
59689                     indexes = [],
59690                     i, parent, index, goingUp;
59691
59692                 // depth-first iterative tree traversal
59693                 while (node || path.length) {
59694
59695                     if (!node) { // go up
59696                         node = path.pop();
59697                         parent = path[path.length - 1];
59698                         i = indexes.pop();
59699                         goingUp = true;
59700                     }
59701
59702                     if (node.leaf) { // check current node
59703                         index = findItem$1(item, node.children, equalsFn);
59704
59705                         if (index !== -1) {
59706                             // item found, remove the item and condense tree upwards
59707                             node.children.splice(index, 1);
59708                             path.push(node);
59709                             this._condense(path);
59710                             return this;
59711                         }
59712                     }
59713
59714                     if (!goingUp && !node.leaf && contains$2(node, bbox)) { // go down
59715                         path.push(node);
59716                         indexes.push(i);
59717                         i = 0;
59718                         parent = node;
59719                         node = node.children[0];
59720
59721                     } else if (parent) { // go right
59722                         i++;
59723                         node = parent.children[i];
59724                         goingUp = false;
59725
59726                     } else node = null; // nothing found
59727                 }
59728
59729                 return this;
59730             },
59731
59732             toBBox: function (item) { return item; },
59733
59734             compareMinX: compareNodeMinX$1,
59735             compareMinY: compareNodeMinY$1,
59736
59737             toJSON: function () { return this.data; },
59738
59739             fromJSON: function (data) {
59740                 this.data = data;
59741                 return this;
59742             },
59743
59744             _all: function (node, result) {
59745                 var nodesToSearch = [];
59746                 while (node) {
59747                     if (node.leaf) result.push.apply(result, node.children);
59748                     else nodesToSearch.push.apply(nodesToSearch, node.children);
59749
59750                     node = nodesToSearch.pop();
59751                 }
59752                 return result;
59753             },
59754
59755             _build: function (items, left, right, height) {
59756
59757                 var N = right - left + 1,
59758                     M = this._maxEntries,
59759                     node;
59760
59761                 if (N <= M) {
59762                     // reached leaf level; return leaf
59763                     node = createNode$1(items.slice(left, right + 1));
59764                     calcBBox$1(node, this.toBBox);
59765                     return node;
59766                 }
59767
59768                 if (!height) {
59769                     // target height of the bulk-loaded tree
59770                     height = Math.ceil(Math.log(N) / Math.log(M));
59771
59772                     // target number of root entries to maximize storage utilization
59773                     M = Math.ceil(N / Math.pow(M, height - 1));
59774                 }
59775
59776                 node = createNode$1([]);
59777                 node.leaf = false;
59778                 node.height = height;
59779
59780                 // split the items into M mostly square tiles
59781
59782                 var N2 = Math.ceil(N / M),
59783                     N1 = N2 * Math.ceil(Math.sqrt(M)),
59784                     i, j, right2, right3;
59785
59786                 multiSelect$1(items, left, right, N1, this.compareMinX);
59787
59788                 for (i = left; i <= right; i += N1) {
59789
59790                     right2 = Math.min(i + N1 - 1, right);
59791
59792                     multiSelect$1(items, i, right2, N2, this.compareMinY);
59793
59794                     for (j = i; j <= right2; j += N2) {
59795
59796                         right3 = Math.min(j + N2 - 1, right2);
59797
59798                         // pack each entry recursively
59799                         node.children.push(this._build(items, j, right3, height - 1));
59800                     }
59801                 }
59802
59803                 calcBBox$1(node, this.toBBox);
59804
59805                 return node;
59806             },
59807
59808             _chooseSubtree: function (bbox, node, level, path) {
59809
59810                 var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
59811
59812                 while (true) {
59813                     path.push(node);
59814
59815                     if (node.leaf || path.length - 1 === level) break;
59816
59817                     minArea = minEnlargement = Infinity;
59818
59819                     for (i = 0, len = node.children.length; i < len; i++) {
59820                         child = node.children[i];
59821                         area = bboxArea$1(child);
59822                         enlargement = enlargedArea$1(bbox, child) - area;
59823
59824                         // choose entry with the least area enlargement
59825                         if (enlargement < minEnlargement) {
59826                             minEnlargement = enlargement;
59827                             minArea = area < minArea ? area : minArea;
59828                             targetNode = child;
59829
59830                         } else if (enlargement === minEnlargement) {
59831                             // otherwise choose one with the smallest area
59832                             if (area < minArea) {
59833                                 minArea = area;
59834                                 targetNode = child;
59835                             }
59836                         }
59837                     }
59838
59839                     node = targetNode || node.children[0];
59840                 }
59841
59842                 return node;
59843             },
59844
59845             _insert: function (item, level, isNode) {
59846
59847                 var toBBox = this.toBBox,
59848                     bbox = isNode ? item : toBBox(item),
59849                     insertPath = [];
59850
59851                 // find the best node for accommodating the item, saving all nodes along the path too
59852                 var node = this._chooseSubtree(bbox, this.data, level, insertPath);
59853
59854                 // put the item into the node
59855                 node.children.push(item);
59856                 extend$3(node, bbox);
59857
59858                 // split on node overflow; propagate upwards if necessary
59859                 while (level >= 0) {
59860                     if (insertPath[level].children.length > this._maxEntries) {
59861                         this._split(insertPath, level);
59862                         level--;
59863                     } else break;
59864                 }
59865
59866                 // adjust bboxes along the insertion path
59867                 this._adjustParentBBoxes(bbox, insertPath, level);
59868             },
59869
59870             // split overflowed node into two
59871             _split: function (insertPath, level) {
59872
59873                 var node = insertPath[level],
59874                     M = node.children.length,
59875                     m = this._minEntries;
59876
59877                 this._chooseSplitAxis(node, m, M);
59878
59879                 var splitIndex = this._chooseSplitIndex(node, m, M);
59880
59881                 var newNode = createNode$1(node.children.splice(splitIndex, node.children.length - splitIndex));
59882                 newNode.height = node.height;
59883                 newNode.leaf = node.leaf;
59884
59885                 calcBBox$1(node, this.toBBox);
59886                 calcBBox$1(newNode, this.toBBox);
59887
59888                 if (level) insertPath[level - 1].children.push(newNode);
59889                 else this._splitRoot(node, newNode);
59890             },
59891
59892             _splitRoot: function (node, newNode) {
59893                 // split root node
59894                 this.data = createNode$1([node, newNode]);
59895                 this.data.height = node.height + 1;
59896                 this.data.leaf = false;
59897                 calcBBox$1(this.data, this.toBBox);
59898             },
59899
59900             _chooseSplitIndex: function (node, m, M) {
59901
59902                 var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
59903
59904                 minOverlap = minArea = Infinity;
59905
59906                 for (i = m; i <= M - m; i++) {
59907                     bbox1 = distBBox$1(node, 0, i, this.toBBox);
59908                     bbox2 = distBBox$1(node, i, M, this.toBBox);
59909
59910                     overlap = intersectionArea$1(bbox1, bbox2);
59911                     area = bboxArea$1(bbox1) + bboxArea$1(bbox2);
59912
59913                     // choose distribution with minimum overlap
59914                     if (overlap < minOverlap) {
59915                         minOverlap = overlap;
59916                         index = i;
59917
59918                         minArea = area < minArea ? area : minArea;
59919
59920                     } else if (overlap === minOverlap) {
59921                         // otherwise choose distribution with minimum area
59922                         if (area < minArea) {
59923                             minArea = area;
59924                             index = i;
59925                         }
59926                     }
59927                 }
59928
59929                 return index;
59930             },
59931
59932             // sorts node children by the best axis for split
59933             _chooseSplitAxis: function (node, m, M) {
59934
59935                 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX$1,
59936                     compareMinY = node.leaf ? this.compareMinY : compareNodeMinY$1,
59937                     xMargin = this._allDistMargin(node, m, M, compareMinX),
59938                     yMargin = this._allDistMargin(node, m, M, compareMinY);
59939
59940                 // if total distributions margin value is minimal for x, sort by minX,
59941                 // otherwise it's already sorted by minY
59942                 if (xMargin < yMargin) node.children.sort(compareMinX);
59943             },
59944
59945             // total margin of all possible split distributions where each node is at least m full
59946             _allDistMargin: function (node, m, M, compare) {
59947
59948                 node.children.sort(compare);
59949
59950                 var toBBox = this.toBBox,
59951                     leftBBox = distBBox$1(node, 0, m, toBBox),
59952                     rightBBox = distBBox$1(node, M - m, M, toBBox),
59953                     margin = bboxMargin$1(leftBBox) + bboxMargin$1(rightBBox),
59954                     i, child;
59955
59956                 for (i = m; i < M - m; i++) {
59957                     child = node.children[i];
59958                     extend$3(leftBBox, node.leaf ? toBBox(child) : child);
59959                     margin += bboxMargin$1(leftBBox);
59960                 }
59961
59962                 for (i = M - m - 1; i >= m; i--) {
59963                     child = node.children[i];
59964                     extend$3(rightBBox, node.leaf ? toBBox(child) : child);
59965                     margin += bboxMargin$1(rightBBox);
59966                 }
59967
59968                 return margin;
59969             },
59970
59971             _adjustParentBBoxes: function (bbox, path, level) {
59972                 // adjust bboxes along the given tree path
59973                 for (var i = level; i >= 0; i--) {
59974                     extend$3(path[i], bbox);
59975                 }
59976             },
59977
59978             _condense: function (path) {
59979                 // go through the path, removing empty nodes and updating bboxes
59980                 for (var i = path.length - 1, siblings; i >= 0; i--) {
59981                     if (path[i].children.length === 0) {
59982                         if (i > 0) {
59983                             siblings = path[i - 1].children;
59984                             siblings.splice(siblings.indexOf(path[i]), 1);
59985
59986                         } else this.clear();
59987
59988                     } else calcBBox$1(path[i], this.toBBox);
59989                 }
59990             },
59991
59992             _initFormat: function (format) {
59993                 // data format (minX, minY, maxX, maxY accessors)
59994
59995                 // uses eval-type function compilation instead of just accepting a toBBox function
59996                 // because the algorithms are very sensitive to sorting functions performance,
59997                 // so they should be dead simple and without inner calls
59998
59999                 var compareArr = ['return a', ' - b', ';'];
60000
60001                 this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
60002                 this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
60003
60004                 this.toBBox = new Function('a',
60005                     'return {minX: a' + format[0] +
60006                     ', minY: a' + format[1] +
60007                     ', maxX: a' + format[2] +
60008                     ', maxY: a' + format[3] + '};');
60009             }
60010         };
60011
60012         function findItem$1(item, items, equalsFn) {
60013             if (!equalsFn) return items.indexOf(item);
60014
60015             for (var i = 0; i < items.length; i++) {
60016                 if (equalsFn(item, items[i])) return i;
60017             }
60018             return -1;
60019         }
60020
60021         // calculate node's bbox from bboxes of its children
60022         function calcBBox$1(node, toBBox) {
60023             distBBox$1(node, 0, node.children.length, toBBox, node);
60024         }
60025
60026         // min bounding rectangle of node children from k to p-1
60027         function distBBox$1(node, k, p, toBBox, destNode) {
60028             if (!destNode) destNode = createNode$1(null);
60029             destNode.minX = Infinity;
60030             destNode.minY = Infinity;
60031             destNode.maxX = -Infinity;
60032             destNode.maxY = -Infinity;
60033
60034             for (var i = k, child; i < p; i++) {
60035                 child = node.children[i];
60036                 extend$3(destNode, node.leaf ? toBBox(child) : child);
60037             }
60038
60039             return destNode;
60040         }
60041
60042         function extend$3(a, b) {
60043             a.minX = Math.min(a.minX, b.minX);
60044             a.minY = Math.min(a.minY, b.minY);
60045             a.maxX = Math.max(a.maxX, b.maxX);
60046             a.maxY = Math.max(a.maxY, b.maxY);
60047             return a;
60048         }
60049
60050         function compareNodeMinX$1(a, b) { return a.minX - b.minX; }
60051         function compareNodeMinY$1(a, b) { return a.minY - b.minY; }
60052
60053         function bboxArea$1(a)   { return (a.maxX - a.minX) * (a.maxY - a.minY); }
60054         function bboxMargin$1(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
60055
60056         function enlargedArea$1(a, b) {
60057             return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
60058                    (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
60059         }
60060
60061         function intersectionArea$1(a, b) {
60062             var minX = Math.max(a.minX, b.minX),
60063                 minY = Math.max(a.minY, b.minY),
60064                 maxX = Math.min(a.maxX, b.maxX),
60065                 maxY = Math.min(a.maxY, b.maxY);
60066
60067             return Math.max(0, maxX - minX) *
60068                    Math.max(0, maxY - minY);
60069         }
60070
60071         function contains$2(a, b) {
60072             return a.minX <= b.minX &&
60073                    a.minY <= b.minY &&
60074                    b.maxX <= a.maxX &&
60075                    b.maxY <= a.maxY;
60076         }
60077
60078         function intersects$1(a, b) {
60079             return b.minX <= a.maxX &&
60080                    b.minY <= a.maxY &&
60081                    b.maxX >= a.minX &&
60082                    b.maxY >= a.minY;
60083         }
60084
60085         function createNode$1(children) {
60086             return {
60087                 children: children,
60088                 height: 1,
60089                 leaf: true,
60090                 minX: Infinity,
60091                 minY: Infinity,
60092                 maxX: -Infinity,
60093                 maxY: -Infinity
60094             };
60095         }
60096
60097         // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
60098         // combines selection algorithm with binary divide & conquer approach
60099
60100         function multiSelect$1(arr, left, right, n, compare) {
60101             var stack = [left, right],
60102                 mid;
60103
60104             while (stack.length) {
60105                 right = stack.pop();
60106                 left = stack.pop();
60107
60108                 if (right - left <= n) continue;
60109
60110                 mid = left + Math.ceil((right - left) / n / 2) * n;
60111                 quickselect$1(arr, mid, left, right, compare);
60112
60113                 stack.push(left, mid, mid, right);
60114             }
60115         }
60116         rbush_1.default = _default$2;
60117
60118         var lineclip_1$1 = lineclip$1;
60119
60120         lineclip$1.polyline = lineclip$1;
60121         lineclip$1.polygon = polygonclip$1;
60122
60123
60124         // Cohen-Sutherland line clippign algorithm, adapted to efficiently
60125         // handle polylines rather than just segments
60126
60127         function lineclip$1(points, bbox, result) {
60128
60129             var len = points.length,
60130                 codeA = bitCode$1(points[0], bbox),
60131                 part = [],
60132                 i, a, b, codeB, lastCode;
60133
60134             if (!result) result = [];
60135
60136             for (i = 1; i < len; i++) {
60137                 a = points[i - 1];
60138                 b = points[i];
60139                 codeB = lastCode = bitCode$1(b, bbox);
60140
60141                 while (true) {
60142
60143                     if (!(codeA | codeB)) { // accept
60144                         part.push(a);
60145
60146                         if (codeB !== lastCode) { // segment went outside
60147                             part.push(b);
60148
60149                             if (i < len - 1) { // start a new line
60150                                 result.push(part);
60151                                 part = [];
60152                             }
60153                         } else if (i === len - 1) {
60154                             part.push(b);
60155                         }
60156                         break;
60157
60158                     } else if (codeA & codeB) { // trivial reject
60159                         break;
60160
60161                     } else if (codeA) { // a outside, intersect with clip edge
60162                         a = intersect$1(a, b, codeA, bbox);
60163                         codeA = bitCode$1(a, bbox);
60164
60165                     } else { // b outside
60166                         b = intersect$1(a, b, codeB, bbox);
60167                         codeB = bitCode$1(b, bbox);
60168                     }
60169                 }
60170
60171                 codeA = lastCode;
60172             }
60173
60174             if (part.length) result.push(part);
60175
60176             return result;
60177         }
60178
60179         // Sutherland-Hodgeman polygon clipping algorithm
60180
60181         function polygonclip$1(points, bbox) {
60182
60183             var result, edge, prev, prevInside, i, p, inside;
60184
60185             // clip against each side of the clip rectangle
60186             for (edge = 1; edge <= 8; edge *= 2) {
60187                 result = [];
60188                 prev = points[points.length - 1];
60189                 prevInside = !(bitCode$1(prev, bbox) & edge);
60190
60191                 for (i = 0; i < points.length; i++) {
60192                     p = points[i];
60193                     inside = !(bitCode$1(p, bbox) & edge);
60194
60195                     // if segment goes through the clip window, add an intersection
60196                     if (inside !== prevInside) result.push(intersect$1(prev, p, edge, bbox));
60197
60198                     if (inside) result.push(p); // add a point if it's inside
60199
60200                     prev = p;
60201                     prevInside = inside;
60202                 }
60203
60204                 points = result;
60205
60206                 if (!points.length) break;
60207             }
60208
60209             return result;
60210         }
60211
60212         // intersect a segment against one of the 4 lines that make up the bbox
60213
60214         function intersect$1(a, b, edge, bbox) {
60215             return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
60216                    edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
60217                    edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
60218                    edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
60219                    null;
60220         }
60221
60222         // bit code reflects the point position relative to the bbox:
60223
60224         //         left  mid  right
60225         //    top  1001  1000  1010
60226         //    mid  0001  0000  0010
60227         // bottom  0101  0100  0110
60228
60229         function bitCode$1(p, bbox) {
60230             var code = 0;
60231
60232             if (p[0] < bbox[0]) code |= 1; // left
60233             else if (p[0] > bbox[2]) code |= 2; // right
60234
60235             if (p[1] < bbox[1]) code |= 4; // bottom
60236             else if (p[1] > bbox[3]) code |= 8; // top
60237
60238             return code;
60239         }
60240
60241         var whichPolygon_1 = whichPolygon;
60242
60243         function whichPolygon(data) {
60244             var bboxes = [];
60245             for (var i = 0; i < data.features.length; i++) {
60246                 var feature = data.features[i];
60247                 var coords = feature.geometry.coordinates;
60248
60249                 if (feature.geometry.type === 'Polygon') {
60250                     bboxes.push(treeItem(coords, feature.properties));
60251
60252                 } else if (feature.geometry.type === 'MultiPolygon') {
60253                     for (var j = 0; j < coords.length; j++) {
60254                         bboxes.push(treeItem(coords[j], feature.properties));
60255                     }
60256                 }
60257             }
60258
60259             var tree = rbush_1().load(bboxes);
60260
60261             function query(p, multi) {
60262                 var output = [],
60263                     result = tree.search({
60264                         minX: p[0],
60265                         minY: p[1],
60266                         maxX: p[0],
60267                         maxY: p[1]
60268                     });
60269                 for (var i = 0; i < result.length; i++) {
60270                     if (insidePolygon(result[i].coords, p)) {
60271                         if (multi)
60272                             output.push(result[i].props);
60273                         else
60274                             return result[i].props;
60275                     }
60276                 }
60277                 return multi && output.length ? output : null;
60278             }
60279
60280             query.tree = tree;
60281             query.bbox = function queryBBox(bbox) {
60282                 var output = [];
60283                 var result = tree.search({
60284                     minX: bbox[0],
60285                     minY: bbox[1],
60286                     maxX: bbox[2],
60287                     maxY: bbox[3]
60288                 });
60289                 for (var i = 0; i < result.length; i++) {
60290                     if (polygonIntersectsBBox(result[i].coords, bbox)) {
60291                         output.push(result[i].props);
60292                     }
60293                 }
60294                 return output;
60295             };
60296
60297             return query;
60298         }
60299
60300         function polygonIntersectsBBox(polygon, bbox) {
60301             var bboxCenter = [
60302                 (bbox[0] + bbox[2]) / 2,
60303                 (bbox[1] + bbox[3]) / 2
60304             ];
60305             if (insidePolygon(polygon, bboxCenter)) return true;
60306             for (var i = 0; i < polygon.length; i++) {
60307                 if (lineclip_1$1(polygon[i], bbox).length > 0) return true;
60308             }
60309             return false;
60310         }
60311
60312         // ray casting algorithm for detecting if point is in polygon
60313         function insidePolygon(rings, p) {
60314             var inside = false;
60315             for (var i = 0, len = rings.length; i < len; i++) {
60316                 var ring = rings[i];
60317                 for (var j = 0, len2 = ring.length, k = len2 - 1; j < len2; k = j++) {
60318                     if (rayIntersect(p, ring[j], ring[k])) inside = !inside;
60319                 }
60320             }
60321             return inside;
60322         }
60323
60324         function rayIntersect(p, p1, p2) {
60325             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]);
60326         }
60327
60328         function treeItem(coords, props) {
60329             var item = {
60330                 minX: Infinity,
60331                 minY: Infinity,
60332                 maxX: -Infinity,
60333                 maxY: -Infinity,
60334                 coords: coords,
60335                 props: props
60336             };
60337
60338             for (var i = 0; i < coords[0].length; i++) {
60339                 var p = coords[0][i];
60340                 item.minX = Math.min(item.minX, p[0]);
60341                 item.minY = Math.min(item.minY, p[1]);
60342                 item.maxX = Math.max(item.maxX, p[0]);
60343                 item.maxY = Math.max(item.maxY, p[1]);
60344             }
60345             return item;
60346         }
60347
60348         var type = "FeatureCollection";
60349         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]]]]}}];
60350         var rawBorders = {
60351         type: type,
60352         features: features
60353         };
60354
60355         let borders = rawBorders;
60356         let whichPolygonGetter = {};
60357         let featuresByCode = {};
60358         let idFilterRegex = /\bThe\b|\bthe\b|\band\b|\bof\b|[-_ .,()&[\]/]/g;
60359         let levels = [
60360           'subterritory',
60361           'territory',
60362           'country',
60363           'intermediateRegion',
60364           'subregion',
60365           'region',
60366           'union',
60367           'world'
60368         ];
60369         loadDerivedDataAndCaches(borders);
60370         function loadDerivedDataAndCaches(borders) {
60371           let identifierProps = ['iso1A2', 'iso1A3', 'm49', 'wikidata', 'emojiFlag', 'nameEn'];
60372           let geometryFeatures = [];
60373           for (let i in borders.features) {
60374             let feature = borders.features[i];
60375             feature.properties.id = feature.properties.iso1A2 || feature.properties.m49;
60376             loadM49(feature);
60377             loadIsoStatus(feature);
60378             loadLevel(feature);
60379             loadGroups(feature);
60380             loadRoadSpeedUnit(feature);
60381             loadDriveSide(feature);
60382             loadFlag(feature);
60383             cacheFeatureByIDs(feature);
60384             if (feature.geometry) geometryFeatures.push(feature);
60385           }
60386           for (let i in borders.features) {
60387             let feature = borders.features[i];
60388             feature.properties.groups.sort(function(groupID1, groupID2) {
60389               return (
60390                 levels.indexOf(featuresByCode[groupID1].properties.level) -
60391                 levels.indexOf(featuresByCode[groupID2].properties.level)
60392               );
60393             });
60394             loadMembersForGroupsOf(feature);
60395           }
60396           let geometryOnlyCollection = {
60397             type: 'RegionFeatureCollection',
60398             features: geometryFeatures
60399           };
60400           whichPolygonGetter = whichPolygon_1(geometryOnlyCollection);
60401           function loadGroups(feature) {
60402             let props = feature.properties;
60403             if (!props.groups) {
60404               props.groups = [];
60405             }
60406             if (props.country) {
60407               props.groups.push(props.country);
60408             }
60409             if (props.m49 !== '001') {
60410               props.groups.push('001');
60411             }
60412           }
60413           function loadM49(feature) {
60414             let props = feature.properties;
60415             if (!props.m49 && props.iso1N3) {
60416               props.m49 = props.iso1N3;
60417             }
60418           }
60419           function loadIsoStatus(feature) {
60420             let props = feature.properties;
60421             if (!props.isoStatus && props.iso1A2) {
60422               props.isoStatus = 'official';
60423             }
60424           }
60425           function loadLevel(feature) {
60426             let props = feature.properties;
60427             if (props.level) return;
60428             if (!props.country) {
60429               props.level = 'country';
60430             } else if (props.isoStatus === 'official') {
60431               props.level = 'territory';
60432             } else {
60433               props.level = 'subterritory';
60434             }
60435           }
60436           function loadRoadSpeedUnit(feature) {
60437             let props = feature.properties;
60438             if (props.roadSpeedUnit === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
60439               props.roadSpeedUnit = 'km/h';
60440             }
60441           }
60442           function loadDriveSide(feature) {
60443             let props = feature.properties;
60444             if (props.driveSide === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
60445               props.driveSide = 'right';
60446             }
60447           }
60448           function loadFlag(feature) {
60449             if (!feature.properties.iso1A2) return;
60450             let flag = feature.properties.iso1A2.replace(/./g, function(char) {
60451               return String.fromCodePoint(char.charCodeAt(0) + 127397);
60452             });
60453             feature.properties.emojiFlag = flag;
60454           }
60455           function loadMembersForGroupsOf(feature) {
60456             let featureID = feature.properties.id;
60457             let standardizedGroupIDs = [];
60458             for (let j in feature.properties.groups) {
60459               let groupID = feature.properties.groups[j];
60460               let groupFeature = featuresByCode[groupID];
60461               standardizedGroupIDs.push(groupFeature.properties.id);
60462               if (groupFeature.properties.members) {
60463                 groupFeature.properties.members.push(featureID);
60464               } else {
60465                 groupFeature.properties.members = [featureID];
60466               }
60467             }
60468             feature.properties.groups = standardizedGroupIDs;
60469           }
60470           function cacheFeatureByIDs(feature) {
60471             for (let k in identifierProps) {
60472               let prop = identifierProps[k];
60473               let id = prop && feature.properties[prop];
60474               if (id) {
60475                 id = id.replace(idFilterRegex, '').toUpperCase();
60476                 featuresByCode[id] = feature;
60477               }
60478             }
60479             if (feature.properties.aliases) {
60480               for (let j in feature.properties.aliases) {
60481                 let alias = feature.properties.aliases[j].replace(idFilterRegex, '').toUpperCase();
60482                 featuresByCode[alias] = feature;
60483               }
60484             }
60485           }
60486         }
60487         function locArray(loc) {
60488           if (Array.isArray(loc)) {
60489             return loc;
60490           } else if (loc.coordinates) {
60491             return loc.coordinates;
60492           }
60493           return loc.geometry.coordinates;
60494         }
60495         function smallestFeature(loc) {
60496           let query = locArray(loc);
60497           let featureProperties = whichPolygonGetter(query);
60498           if (!featureProperties) return null;
60499           return featuresByCode[featureProperties.id];
60500         }
60501         function countryFeature(loc) {
60502           let feature = smallestFeature(loc);
60503           if (!feature) return null;
60504           let countryCode = feature.properties.country || feature.properties.iso1A2;
60505           return featuresByCode[countryCode];
60506         }
60507         function featureForLoc(loc, opts) {
60508           if (opts && opts.level && opts.level !== 'country') {
60509             let features = featuresContaining(loc);
60510             let targetLevel = opts.level;
60511             let targetLevelIndex = levels.indexOf(targetLevel);
60512             if (targetLevelIndex === -1) return null;
60513             for (let i in features) {
60514               let feature = features[i];
60515               if (
60516                 feature.properties.level === targetLevel ||
60517                 levels.indexOf(feature.properties.level) > targetLevelIndex
60518               ) {
60519                 return feature;
60520               }
60521             }
60522             return null;
60523           }
60524           return countryFeature(loc);
60525         }
60526         function featureForID(id) {
60527           let stringID;
60528           if (typeof id === 'number') {
60529             stringID = id.toString();
60530             if (stringID.length === 1) {
60531               stringID = '00' + stringID;
60532             } else if (stringID.length === 2) {
60533               stringID = '0' + stringID;
60534             }
60535           } else {
60536             stringID = id.replace(idFilterRegex, '').toUpperCase();
60537           }
60538           return featuresByCode[stringID] || null;
60539         }
60540         function smallestOrMatchingFeature(query) {
60541           if (typeof query === 'object') {
60542             return smallestFeature(query);
60543           }
60544           return featureForID(query);
60545         }
60546         function feature(query, opts) {
60547           if (typeof query === 'object') {
60548             return featureForLoc(query, opts);
60549           }
60550           return featureForID(query);
60551         }
60552         function iso1A2Code(query, opts) {
60553           let match = feature(query, opts);
60554           if (!match) return null;
60555           return match.properties.iso1A2 || null;
60556         }
60557         function featuresContaining(query, strict) {
60558           let feature = smallestOrMatchingFeature(query);
60559           if (!feature) return [];
60560           let features = [];
60561           if (!strict || typeof query === 'object') {
60562             features.push(feature);
60563           }
60564           let properties = feature.properties;
60565           for (let i in properties.groups) {
60566             let groupID = properties.groups[i];
60567             features.push(featuresByCode[groupID]);
60568           }
60569           return features;
60570         }
60571         function roadSpeedUnit(query) {
60572           let feature = smallestOrMatchingFeature(query);
60573           return (feature && feature.properties.roadSpeedUnit) || null;
60574         }
60575
60576         let _dataDeprecated;
60577         let _nsi;
60578
60579         function validationOutdatedTags() {
60580           const type = 'outdated_tags';
60581           const nsiKeys = ['amenity', 'shop', 'tourism', 'leisure', 'office'];
60582
60583           // A concern here in switching to async data means that `_dataDeprecated`
60584           // and `_nsi` will not be available at first, so the data on early tiles
60585           // may not have tags validated fully.
60586
60587           // initialize deprecated tags array
60588           _mainFileFetcher.get('deprecated')
60589             .then(d => _dataDeprecated = d)
60590             .catch(() => { /* ignore */ });
60591
60592           _mainFileFetcher.get('nsi_brands')
60593             .then(d => {
60594               _nsi = {
60595                 brands: d.brands,
60596                 matcher: matcher$1(),
60597                 wikidata: {},
60598                 wikipedia: {}
60599               };
60600
60601               // initialize name-suggestion-index matcher
60602               _nsi.matcher.buildMatchIndex(d.brands);
60603
60604               // index all known wikipedia and wikidata tags
60605               Object.keys(d.brands).forEach(kvnd => {
60606                 const brand = d.brands[kvnd];
60607                 const wd = brand.tags['brand:wikidata'];
60608                 const wp = brand.tags['brand:wikipedia'];
60609                 if (wd) { _nsi.wikidata[wd] = kvnd; }
60610                 if (wp) { _nsi.wikipedia[wp] = kvnd; }
60611               });
60612
60613               return _nsi;
60614             })
60615             .catch(() => { /* ignore */ });
60616
60617
60618           function oldTagIssues(entity, graph) {
60619             const oldTags = Object.assign({}, entity.tags);  // shallow copy
60620             let preset = _mainPresetIndex.match(entity, graph);
60621             let subtype = 'deprecated_tags';
60622             if (!preset) return [];
60623
60624             // upgrade preset..
60625             if (preset.replacement) {
60626               const newPreset = _mainPresetIndex.item(preset.replacement);
60627               graph = actionChangePreset(entity.id, preset, newPreset, true /* skip field defaults */)(graph);
60628               entity = graph.entity(entity.id);
60629               preset = newPreset;
60630             }
60631
60632             // upgrade tags..
60633             if (_dataDeprecated) {
60634               const deprecatedTags = entity.deprecatedTags(_dataDeprecated);
60635               if (deprecatedTags.length) {
60636                 deprecatedTags.forEach(tag => {
60637                   graph = actionUpgradeTags(entity.id, tag.old, tag.replace)(graph);
60638                 });
60639                 entity = graph.entity(entity.id);
60640               }
60641             }
60642
60643             // add missing addTags..
60644             let newTags = Object.assign({}, entity.tags);  // shallow copy
60645             if (preset.tags !== preset.addTags) {
60646               Object.keys(preset.addTags).forEach(k => {
60647                 if (!newTags[k]) {
60648                   if (preset.addTags[k] === '*') {
60649                     newTags[k] = 'yes';
60650                   } else {
60651                     newTags[k] = preset.addTags[k];
60652                   }
60653                 }
60654               });
60655             }
60656
60657             if (_nsi) {
60658               // Do `wikidata` or `wikipedia` identify this entity as a brand?  #6416
60659               // If so, these tags can be swapped to `brand:wikidata`/`brand:wikipedia`
60660               let isBrand;
60661               if (newTags.wikidata) {                 // try matching `wikidata`
60662                 isBrand = _nsi.wikidata[newTags.wikidata];
60663               }
60664               if (!isBrand && newTags.wikipedia) {    // fallback to `wikipedia`
60665                 isBrand = _nsi.wikipedia[newTags.wikipedia];
60666               }
60667               if (isBrand && !newTags.office) {       // but avoid doing this for corporate offices
60668                 if (newTags.wikidata) {
60669                   newTags['brand:wikidata'] = newTags.wikidata;
60670                   delete newTags.wikidata;
60671                 }
60672                 if (newTags.wikipedia) {
60673                   newTags['brand:wikipedia'] = newTags.wikipedia;
60674                   delete newTags.wikipedia;
60675                 }
60676                 // I considered setting `name` and other tags here, but they aren't unique per wikidata
60677                 // (Q2759586 -> in USA "Papa John's", in Russia "Папа Джонс")
60678                 // So users will really need to use a preset or assign `name` themselves.
60679               }
60680
60681               // try key/value|name match against name-suggestion-index
60682               if (newTags.name) {
60683                 for (let i = 0; i < nsiKeys.length; i++) {
60684                   const k = nsiKeys[i];
60685                   if (!newTags[k]) continue;
60686
60687                   const center = entity.extent(graph).center();
60688                   const countryCode = iso1A2Code(center);
60689                   const match = _nsi.matcher.matchKVN(k, newTags[k], newTags.name, countryCode && countryCode.toLowerCase());
60690                   if (!match) continue;
60691
60692                   // for now skip ambiguous matches (like Target~(USA) vs Target~(Australia))
60693                   if (match.d) continue;
60694
60695                   const brand = _nsi.brands[match.kvnd];
60696                   if (brand && brand.tags['brand:wikidata'] &&
60697                     brand.tags['brand:wikidata'] !== entity.tags['not:brand:wikidata']) {
60698                     subtype = 'noncanonical_brand';
60699
60700                     const keepTags = ['takeaway'].reduce((acc, k) => {
60701                       if (newTags[k]) {
60702                         acc[k] = newTags[k];
60703                       }
60704                       return acc;
60705                     }, {});
60706
60707                     nsiKeys.forEach(k => delete newTags[k]);
60708                     Object.assign(newTags, brand.tags, keepTags);
60709                     break;
60710                   }
60711                 }
60712               }
60713             }
60714
60715             // determine diff
60716             const tagDiff = utilTagDiff(oldTags, newTags);
60717             if (!tagDiff.length) return [];
60718
60719             const isOnlyAddingTags = tagDiff.every(d => d.type === '+');
60720
60721             let prefix = '';
60722             if (subtype === 'noncanonical_brand') {
60723               prefix = 'noncanonical_brand.';
60724             } else if (subtype === 'deprecated_tags' && isOnlyAddingTags) {
60725               subtype = 'incomplete_tags';
60726               prefix = 'incomplete.';
60727             }
60728
60729             // don't allow autofixing brand tags
60730             let autoArgs = subtype !== 'noncanonical_brand' ? [doUpgrade, _t('issues.fix.upgrade_tags.annotation')] : null;
60731
60732             return [new validationIssue({
60733               type: type,
60734               subtype: subtype,
60735               severity: 'warning',
60736               message: showMessage,
60737               reference: showReference,
60738               entityIds: [entity.id],
60739               hash: JSON.stringify(tagDiff),
60740               dynamicFixes: () => {
60741                 return [
60742                   new validationIssueFix({
60743                     autoArgs: autoArgs,
60744                     title: _t('issues.fix.upgrade_tags.title'),
60745                     onClick: (context) => {
60746                       context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
60747                     }
60748                   })
60749                 ];
60750               }
60751             })];
60752
60753
60754             function doUpgrade(graph) {
60755               const currEntity = graph.hasEntity(entity.id);
60756               if (!currEntity) return graph;
60757
60758               let newTags = Object.assign({}, currEntity.tags);  // shallow copy
60759               tagDiff.forEach(diff => {
60760                 if (diff.type === '-') {
60761                   delete newTags[diff.key];
60762                 } else if (diff.type === '+') {
60763                   newTags[diff.key] = diff.newVal;
60764                 }
60765               });
60766
60767               return actionChangeTags(currEntity.id, newTags)(graph);
60768             }
60769
60770
60771             function showMessage(context) {
60772               const currEntity = context.hasEntity(entity.id);
60773               if (!currEntity) return '';
60774
60775               let messageID = `issues.outdated_tags.${prefix}message`;
60776               if (subtype === 'noncanonical_brand' && isOnlyAddingTags) {
60777                 messageID += '_incomplete';
60778               }
60779               return _t(messageID, { feature: utilDisplayLabel(currEntity, context.graph()) });
60780             }
60781
60782
60783             function showReference(selection) {
60784               let enter = selection.selectAll('.issue-reference')
60785                 .data([0])
60786                 .enter();
60787
60788               enter
60789                 .append('div')
60790                 .attr('class', 'issue-reference')
60791                 .text(_t(`issues.outdated_tags.${prefix}reference`));
60792
60793               enter
60794                 .append('strong')
60795                 .text(_t('issues.suggested'));
60796
60797               enter
60798                 .append('table')
60799                 .attr('class', 'tagDiff-table')
60800                 .selectAll('.tagDiff-row')
60801                 .data(tagDiff)
60802                 .enter()
60803                 .append('tr')
60804                 .attr('class', 'tagDiff-row')
60805                 .append('td')
60806                 .attr('class', d => {
60807                   let klass = d.type === '+' ? 'add' : 'remove';
60808                   return `tagDiff-cell tagDiff-cell-${klass}`;
60809                 })
60810                 .text(d => d.display);
60811             }
60812           }
60813
60814
60815           function oldMultipolygonIssues(entity, graph) {
60816             let multipolygon, outerWay;
60817             if (entity.type === 'relation') {
60818               outerWay = osmOldMultipolygonOuterMemberOfRelation(entity, graph);
60819               multipolygon = entity;
60820             } else if (entity.type === 'way') {
60821               multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
60822               outerWay = entity;
60823             } else {
60824               return [];
60825             }
60826
60827             if (!multipolygon || !outerWay) return [];
60828
60829             return [new validationIssue({
60830               type: type,
60831               subtype: 'old_multipolygon',
60832               severity: 'warning',
60833               message: showMessage,
60834               reference: showReference,
60835               entityIds: [outerWay.id, multipolygon.id],
60836               dynamicFixes: () => {
60837                 return [
60838                   new validationIssueFix({
60839                     autoArgs: [doUpgrade, _t('issues.fix.move_tags.annotation')],
60840                     title: _t('issues.fix.move_tags.title'),
60841                     onClick: (context) => {
60842                       context.perform(doUpgrade, _t('issues.fix.move_tags.annotation'));
60843                     }
60844                   })
60845                 ];
60846               }
60847             })];
60848
60849
60850             function doUpgrade(graph) {
60851               let currMultipolygon = graph.hasEntity(multipolygon.id);
60852               let currOuterWay = graph.hasEntity(outerWay.id);
60853               if (!currMultipolygon || !currOuterWay) return graph;
60854
60855               currMultipolygon = currMultipolygon.mergeTags(currOuterWay.tags);
60856               graph = graph.replace(currMultipolygon);
60857               return actionChangeTags(currOuterWay.id, {})(graph);
60858             }
60859
60860
60861             function showMessage(context) {
60862               let currMultipolygon = context.hasEntity(multipolygon.id);
60863               if (!currMultipolygon) return '';
60864
60865               return _t('issues.old_multipolygon.message',
60866                   { multipolygon: utilDisplayLabel(currMultipolygon, context.graph()) }
60867               );
60868             }
60869
60870
60871             function showReference(selection) {
60872               selection.selectAll('.issue-reference')
60873                 .data([0])
60874                 .enter()
60875                 .append('div')
60876                 .attr('class', 'issue-reference')
60877                 .text(_t('issues.old_multipolygon.reference'));
60878             }
60879           }
60880
60881
60882           let validation = function checkOutdatedTags(entity, graph) {
60883             let issues = oldMultipolygonIssues(entity, graph);
60884             if (!issues.length) issues = oldTagIssues(entity, graph);
60885             return issues;
60886           };
60887
60888
60889           validation.type = type;
60890
60891           return validation;
60892         }
60893
60894         function validationPrivateData() {
60895             var type = 'private_data';
60896
60897             // assume that some buildings are private
60898             var privateBuildingValues = {
60899                 detached: true,
60900                 farm: true,
60901                 house: true,
60902                 houseboat: true,
60903                 residential: true,
60904                 semidetached_house: true,
60905                 static_caravan: true
60906             };
60907
60908             // but they might be public if they have one of these other tags
60909             var publicKeys = {
60910                 amenity: true,
60911                 craft: true,
60912                 historic: true,
60913                 leisure: true,
60914                 office: true,
60915                 shop: true,
60916                 tourism: true
60917             };
60918
60919             // these tags may contain personally identifying info
60920             var personalTags = {
60921                 'contact:email': true,
60922                 'contact:fax': true,
60923                 'contact:phone': true,
60924                 email: true,
60925                 fax: true,
60926                 phone: true
60927             };
60928
60929
60930             var validation = function checkPrivateData(entity) {
60931                 var tags = entity.tags;
60932                 if (!tags.building || !privateBuildingValues[tags.building]) return [];
60933
60934                 var keepTags = {};
60935                 for (var k in tags) {
60936                     if (publicKeys[k]) return [];  // probably a public feature
60937                     if (!personalTags[k]) {
60938                         keepTags[k] = tags[k];
60939                     }
60940                 }
60941
60942                 var tagDiff = utilTagDiff(tags, keepTags);
60943                 if (!tagDiff.length) return [];
60944
60945                 var fixID = tagDiff.length === 1 ? 'remove_tag' : 'remove_tags';
60946
60947                 return [new validationIssue({
60948                     type: type,
60949                     severity: 'warning',
60950                     message: showMessage,
60951                     reference: showReference,
60952                     entityIds: [entity.id],
60953                     dynamicFixes: function() {
60954                         return [
60955                             new validationIssueFix({
60956                                 icon: 'iD-operation-delete',
60957                                 title: _t('issues.fix.' + fixID + '.title'),
60958                                 onClick: function(context) {
60959                                     context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
60960                                 }
60961                             })
60962                         ];
60963                     }
60964                 })];
60965
60966
60967                 function doUpgrade(graph) {
60968                     var currEntity = graph.hasEntity(entity.id);
60969                     if (!currEntity) return graph;
60970
60971                     var newTags = Object.assign({}, currEntity.tags);  // shallow copy
60972                     tagDiff.forEach(function(diff) {
60973                         if (diff.type === '-') {
60974                             delete newTags[diff.key];
60975                         } else if (diff.type === '+') {
60976                             newTags[diff.key] = diff.newVal;
60977                         }
60978                     });
60979
60980                     return actionChangeTags(currEntity.id, newTags)(graph);
60981                 }
60982
60983
60984                 function showMessage(context) {
60985                     var currEntity = context.hasEntity(this.entityIds[0]);
60986                     if (!currEntity) return '';
60987
60988                     return _t('issues.private_data.contact.message',
60989                         { feature: utilDisplayLabel(currEntity, context.graph()) }
60990                     );
60991                 }
60992
60993
60994                 function showReference(selection) {
60995                     var enter = selection.selectAll('.issue-reference')
60996                         .data([0])
60997                         .enter();
60998
60999                     enter
61000                         .append('div')
61001                         .attr('class', 'issue-reference')
61002                         .text(_t('issues.private_data.reference'));
61003
61004                     enter
61005                         .append('strong')
61006                         .text(_t('issues.suggested'));
61007
61008                     enter
61009                         .append('table')
61010                         .attr('class', 'tagDiff-table')
61011                         .selectAll('.tagDiff-row')
61012                         .data(tagDiff)
61013                         .enter()
61014                         .append('tr')
61015                         .attr('class', 'tagDiff-row')
61016                         .append('td')
61017                         .attr('class', function(d) {
61018                             var klass = d.type === '+' ? 'add' : 'remove';
61019                             return 'tagDiff-cell tagDiff-cell-' + klass;
61020                         })
61021                         .text(function(d) { return d.display; });
61022                 }
61023             };
61024
61025
61026             validation.type = type;
61027
61028             return validation;
61029         }
61030
61031         let _discardNameRegexes = [];
61032
61033         function validationSuspiciousName() {
61034           const type = 'suspicious_name';
61035           const keysToTestForGenericValues = [
61036             'aerialway', 'aeroway', 'amenity', 'building', 'craft', 'highway',
61037             'leisure', 'railway', 'man_made', 'office', 'shop', 'tourism', 'waterway'
61038           ];
61039
61040           // A concern here in switching to async data means that `_nsiFilters` will not
61041           // be available at first, so the data on early tiles may not have tags validated fully.
61042
61043           _mainFileFetcher.get('nsi_filters')
61044             .then(filters => {
61045               // known list of generic names (e.g. "bar")
61046               _discardNameRegexes = filters.discardNames
61047                 .map(discardName => new RegExp(discardName, 'i'));
61048             })
61049             .catch(() => { /* ignore */ });
61050
61051
61052           function isDiscardedSuggestionName(lowercaseName) {
61053             return _discardNameRegexes.some(regex => regex.test(lowercaseName));
61054           }
61055
61056           // test if the name is just the key or tag value (e.g. "park")
61057           function nameMatchesRawTag(lowercaseName, tags) {
61058             for (let i = 0; i < keysToTestForGenericValues.length; i++) {
61059               let key = keysToTestForGenericValues[i];
61060               let val = tags[key];
61061               if (val) {
61062                 val = val.toLowerCase();
61063                 if (key === lowercaseName ||
61064                   val === lowercaseName ||
61065                   key.replace(/\_/g, ' ') === lowercaseName ||
61066                   val.replace(/\_/g, ' ') === lowercaseName) {
61067                   return true;
61068                 }
61069               }
61070             }
61071             return false;
61072           }
61073
61074           function isGenericName(name, tags) {
61075             name = name.toLowerCase();
61076             return nameMatchesRawTag(name, tags) || isDiscardedSuggestionName(name);
61077           }
61078
61079           function makeGenericNameIssue(entityId, nameKey, genericName, langCode) {
61080             return new validationIssue({
61081               type: type,
61082               subtype: 'generic_name',
61083               severity: 'warning',
61084               message: function(context) {
61085                 let entity = context.hasEntity(this.entityIds[0]);
61086                 if (!entity) return '';
61087                 let preset = _mainPresetIndex.match(entity, context.graph());
61088                 let langName = langCode && _mainLocalizer.languageName(langCode);
61089                 return _t('issues.generic_name.message' + (langName ? '_language' : ''),
61090                   { feature: preset.name(), name: genericName, language: langName }
61091                 );
61092               },
61093               reference: showReference,
61094               entityIds: [entityId],
61095               hash: nameKey + '=' + genericName,
61096               dynamicFixes: function() {
61097                 return [
61098                   new validationIssueFix({
61099                     icon: 'iD-operation-delete',
61100                     title: _t('issues.fix.remove_the_name.title'),
61101                     onClick: function(context) {
61102                       let entityId = this.issue.entityIds[0];
61103                       let entity = context.entity(entityId);
61104                       let tags = Object.assign({}, entity.tags);   // shallow copy
61105                       delete tags[nameKey];
61106                       context.perform(
61107                         actionChangeTags(entityId, tags), _t('issues.fix.remove_generic_name.annotation')
61108                       );
61109                     }
61110                   })
61111                 ];
61112               }
61113             });
61114
61115             function showReference(selection) {
61116               selection.selectAll('.issue-reference')
61117                 .data([0])
61118                 .enter()
61119                 .append('div')
61120                 .attr('class', 'issue-reference')
61121                 .text(_t('issues.generic_name.reference'));
61122             }
61123           }
61124
61125           function makeIncorrectNameIssue(entityId, nameKey, incorrectName, langCode) {
61126             return new validationIssue({
61127               type: type,
61128               subtype: 'not_name',
61129               severity: 'warning',
61130               message: function(context) {
61131                 const entity = context.hasEntity(this.entityIds[0]);
61132                 if (!entity) return '';
61133                 const preset = _mainPresetIndex.match(entity, context.graph());
61134                 const langName = langCode && _mainLocalizer.languageName(langCode);
61135                 return _t('issues.incorrect_name.message' + (langName ? '_language' : ''),
61136                   { feature: preset.name(), name: incorrectName, language: langName }
61137                 );
61138               },
61139               reference: showReference,
61140               entityIds: [entityId],
61141               hash: nameKey + '=' + incorrectName,
61142               dynamicFixes: function() {
61143                 return [
61144                   new validationIssueFix({
61145                     icon: 'iD-operation-delete',
61146                     title: _t('issues.fix.remove_the_name.title'),
61147                     onClick: function(context) {
61148                       const entityId = this.issue.entityIds[0];
61149                       const entity = context.entity(entityId);
61150                       let tags = Object.assign({}, entity.tags);   // shallow copy
61151                       delete tags[nameKey];
61152                       context.perform(
61153                         actionChangeTags(entityId, tags), _t('issues.fix.remove_mistaken_name.annotation')
61154                       );
61155                     }
61156                   })
61157                 ];
61158               }
61159             });
61160
61161             function showReference(selection) {
61162               selection.selectAll('.issue-reference')
61163                 .data([0])
61164                 .enter()
61165                 .append('div')
61166                 .attr('class', 'issue-reference')
61167                 .text(_t('issues.generic_name.reference'));
61168             }
61169           }
61170
61171
61172           let validation = function checkGenericName(entity) {
61173             // a generic name is okay if it's a known brand or entity
61174             if (entity.hasWikidata()) return [];
61175
61176             let issues = [];
61177             const notNames = (entity.tags['not:name'] || '').split(';');
61178
61179             for (let key in entity.tags) {
61180               const m = key.match(/^name(?:(?::)([a-zA-Z_-]+))?$/);
61181               if (!m) continue;
61182
61183               const langCode = m.length >= 2 ? m[1] : null;
61184               const value = entity.tags[key];
61185               if (notNames.length) {
61186                 for (let i in notNames) {
61187                   const notName = notNames[i];
61188                   if (notName && value === notName) {
61189                     issues.push(makeIncorrectNameIssue(entity.id, key, value, langCode));
61190                     continue;
61191                   }
61192                 }
61193               }
61194               if (isGenericName(value, entity.tags)) {
61195                 issues.push(makeGenericNameIssue(entity.id, key, value, langCode));
61196               }
61197             }
61198
61199             return issues;
61200           };
61201
61202
61203           validation.type = type;
61204
61205           return validation;
61206         }
61207
61208         function validationUnsquareWay(context) {
61209             var type = 'unsquare_way';
61210             var DEFAULT_DEG_THRESHOLD = 5;   // see also issues.js
61211
61212             // use looser epsilon for detection to reduce warnings of buildings that are essentially square already
61213             var epsilon = 0.05;
61214             var nodeThreshold = 10;
61215
61216             function isBuilding(entity, graph) {
61217                 if (entity.type !== 'way' || entity.geometry(graph) !== 'area') return false;
61218                 return entity.tags.building && entity.tags.building !== 'no';
61219             }
61220
61221
61222             var validation = function checkUnsquareWay(entity, graph) {
61223
61224                 if (!isBuilding(entity, graph)) return [];
61225
61226                 // don't flag ways marked as physically unsquare
61227                 if (entity.tags.nonsquare === 'yes') return [];
61228
61229                 var isClosed = entity.isClosed();
61230                 if (!isClosed) return [];        // this building has bigger problems
61231
61232                 // don't flag ways with lots of nodes since they are likely detail-mapped
61233                 var nodes = graph.childNodes(entity).slice();    // shallow copy
61234                 if (nodes.length > nodeThreshold + 1) return [];   // +1 because closing node appears twice
61235
61236                 // ignore if not all nodes are fully downloaded
61237                 var osm = services.osm;
61238                 if (!osm || nodes.some(function(node) { return !osm.isDataLoaded(node.loc); })) return [];
61239
61240                 // don't flag connected ways to avoid unresolvable unsquare loops
61241                 var hasConnectedSquarableWays = nodes.some(function(node) {
61242                     return graph.parentWays(node).some(function(way) {
61243                         if (way.id === entity.id) return false;
61244                         if (isBuilding(way, graph)) return true;
61245                         return graph.parentRelations(way).some(function(parentRelation) {
61246                             return parentRelation.isMultipolygon() &&
61247                                 parentRelation.tags.building &&
61248                                 parentRelation.tags.building !== 'no';
61249                         });
61250                     });
61251                 });
61252                 if (hasConnectedSquarableWays) return [];
61253
61254
61255                 // user-configurable square threshold
61256                 var storedDegreeThreshold = corePreferences('validate-square-degrees');
61257                 var degreeThreshold = isNaN(storedDegreeThreshold) ? DEFAULT_DEG_THRESHOLD : parseFloat(storedDegreeThreshold);
61258
61259                 var points = nodes.map(function(node) { return context.projection(node.loc); });
61260                 if (!geoOrthoCanOrthogonalize(points, isClosed, epsilon, degreeThreshold, true)) return [];
61261
61262                 var autoArgs;
61263                 // don't allow autosquaring features linked to wikidata
61264                 if (!entity.tags.wikidata) {
61265                     // use same degree threshold as for detection
61266                     var autoAction = actionOrthogonalize(entity.id, context.projection, undefined, degreeThreshold);
61267                     autoAction.transitionable = false;  // when autofixing, do it instantly
61268                     autoArgs = [autoAction, _t('operations.orthogonalize.annotation.feature.single')];
61269                 }
61270
61271                 return [new validationIssue({
61272                     type: type,
61273                     subtype: 'building',
61274                     severity: 'warning',
61275                     message: function(context) {
61276                         var entity = context.hasEntity(this.entityIds[0]);
61277                         return entity ? _t('issues.unsquare_way.message', { feature: utilDisplayLabel(entity, context.graph()) }) : '';
61278                     },
61279                     reference: showReference,
61280                     entityIds: [entity.id],
61281                     hash: JSON.stringify(autoArgs !== undefined) + degreeThreshold,
61282                     dynamicFixes: function() {
61283                         return [
61284                             new validationIssueFix({
61285                                 icon: 'iD-operation-orthogonalize',
61286                                 title: _t('issues.fix.square_feature.title'),
61287                                 autoArgs: autoArgs,
61288                                 onClick: function(context, completionHandler) {
61289                                     var entityId = this.issue.entityIds[0];
61290                                     // use same degree threshold as for detection
61291                                     context.perform(
61292                                         actionOrthogonalize(entityId, context.projection, undefined, degreeThreshold),
61293                                         _t('operations.orthogonalize.annotation.feature.single')
61294                                     );
61295                                     // run after the squaring transition (currently 150ms)
61296                                     window.setTimeout(function() { completionHandler(); }, 175);
61297                                 }
61298                             }),
61299                             /*
61300                             new validationIssueFix({
61301                                 title: t('issues.fix.tag_as_unsquare.title'),
61302                                 onClick: function(context) {
61303                                     var entityId = this.issue.entityIds[0];
61304                                     var entity = context.entity(entityId);
61305                                     var tags = Object.assign({}, entity.tags);  // shallow copy
61306                                     tags.nonsquare = 'yes';
61307                                     context.perform(
61308                                         actionChangeTags(entityId, tags),
61309                                         t('issues.fix.tag_as_unsquare.annotation')
61310                                     );
61311                                 }
61312                             })
61313                             */
61314                         ];
61315                     }
61316                 })];
61317
61318                 function showReference(selection) {
61319                     selection.selectAll('.issue-reference')
61320                         .data([0])
61321                         .enter()
61322                         .append('div')
61323                         .attr('class', 'issue-reference')
61324                         .text(_t('issues.unsquare_way.buildings.reference'));
61325                 }
61326             };
61327
61328             validation.type = type;
61329
61330             return validation;
61331         }
61332
61333         var Validations = /*#__PURE__*/Object.freeze({
61334                 __proto__: null,
61335                 validationAlmostJunction: validationAlmostJunction,
61336                 validationCloseNodes: validationCloseNodes,
61337                 validationCrossingWays: validationCrossingWays,
61338                 validationDisconnectedWay: validationDisconnectedWay,
61339                 validationFormatting: validationFormatting,
61340                 validationHelpRequest: validationHelpRequest,
61341                 validationImpossibleOneway: validationImpossibleOneway,
61342                 validationIncompatibleSource: validationIncompatibleSource,
61343                 validationMaprules: validationMaprules,
61344                 validationMismatchedGeometry: validationMismatchedGeometry,
61345                 validationMissingRole: validationMissingRole,
61346                 validationMissingTag: validationMissingTag,
61347                 validationOutdatedTags: validationOutdatedTags,
61348                 validationPrivateData: validationPrivateData,
61349                 validationSuspiciousName: validationSuspiciousName,
61350                 validationUnsquareWay: validationUnsquareWay
61351         });
61352
61353         function coreValidator(context) {
61354             var dispatch$1 = dispatch('validated', 'focusedIssue');
61355             var validator = utilRebind({}, dispatch$1, 'on');
61356
61357             var _rules = {};
61358             var _disabledRules = {};
61359
61360             var _ignoredIssueIDs = {};          // issue.id -> true
61361             var _baseCache = validationCache(); // issues before any user edits
61362             var _headCache = validationCache(); // issues after all user edits
61363             var _validatedGraph = null;
61364             var _deferred = new Set();
61365
61366             //
61367             // initialize the validator rulesets
61368             //
61369             validator.init = function() {
61370                 Object.values(Validations).forEach(function(validation) {
61371                     if (typeof validation !== 'function') return;
61372
61373                     var fn = validation(context);
61374                     var key = fn.type;
61375                     _rules[key] = fn;
61376                 });
61377
61378                 var disabledRules = corePreferences('validate-disabledRules');
61379                 if (disabledRules) {
61380                     disabledRules.split(',')
61381                         .forEach(function(key) { _disabledRules[key] = true; });
61382                 }
61383             };
61384
61385
61386             //
61387             // clear caches, called whenever iD resets after a save
61388             //
61389             validator.reset = function() {
61390                 Array.from(_deferred).forEach(function(handle) {
61391                     window.cancelIdleCallback(handle);
61392                     _deferred.delete(handle);
61393                 });
61394
61395                 // clear caches
61396                 _ignoredIssueIDs = {};
61397                 _baseCache = validationCache();
61398                 _headCache = validationCache();
61399                 _validatedGraph = null;
61400             };
61401
61402             validator.resetIgnoredIssues = function() {
61403                 _ignoredIssueIDs = {};
61404                 // reload UI
61405                 dispatch$1.call('validated');
61406             };
61407
61408
61409             // must update issues when the user changes the unsquare thereshold
61410             validator.reloadUnsquareIssues = function() {
61411
61412                 reloadUnsquareIssues(_headCache, context.graph());
61413                 reloadUnsquareIssues(_baseCache, context.history().base());
61414
61415                 dispatch$1.call('validated');
61416             };
61417
61418             function reloadUnsquareIssues(cache, graph) {
61419
61420                 var checkUnsquareWay = _rules.unsquare_way;
61421                 if (typeof checkUnsquareWay !== 'function') return;
61422
61423                 // uncache existing
61424                 cache.uncacheIssuesOfType('unsquare_way');
61425
61426                 var buildings = context.history().tree().intersects(geoExtent([-180,-90],[180, 90]), graph)  // everywhere
61427                     .filter(function(entity) {
61428                         return entity.type === 'way' && entity.tags.building && entity.tags.building !== 'no';
61429                     });
61430
61431                 // rerun for all buildings
61432                 buildings.forEach(function(entity) {
61433                     var detected = checkUnsquareWay(entity, graph);
61434                     if (detected.length !== 1) return;
61435                     var issue = detected[0];
61436                     if (!cache.issuesByEntityID[entity.id]) {
61437                         cache.issuesByEntityID[entity.id] = new Set();
61438                     }
61439                     cache.issuesByEntityID[entity.id].add(issue.id);
61440                     cache.issuesByIssueID[issue.id] = issue;
61441                 });
61442             }
61443
61444             // options = {
61445             //     what: 'all',     // 'all' or 'edited'
61446             //     where: 'all',   // 'all' or 'visible'
61447             //     includeIgnored: false   // true, false, or 'only'
61448             //     includeDisabledRules: false   // true, false, or 'only'
61449             // };
61450             validator.getIssues = function(options) {
61451                 var opts = Object.assign({ what: 'all', where: 'all', includeIgnored: false, includeDisabledRules: false }, options);
61452                 var issues = Object.values(_headCache.issuesByIssueID);
61453                 var view = context.map().extent();
61454
61455                 return issues.filter(function(issue) {
61456                     if (!issue) return false;
61457                     if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false;
61458                     if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false;
61459
61460                     if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) return false;
61461                     if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) return false;
61462
61463                     // Sanity check:  This issue may be for an entity that not longer exists.
61464                     // If we detect this, uncache and return false so it is not included..
61465                     var entityIds = issue.entityIds || [];
61466                     for (var i = 0; i < entityIds.length; i++) {
61467                         var entityId = entityIds[i];
61468                         if (!context.hasEntity(entityId)) {
61469                             delete _headCache.issuesByEntityID[entityId];
61470                             delete _headCache.issuesByIssueID[issue.id];
61471                             return false;
61472                         }
61473                     }
61474
61475                     if (opts.what === 'edited' && _baseCache.issuesByIssueID[issue.id]) return false;
61476
61477                     if (opts.where === 'visible') {
61478                         var extent = issue.extent(context.graph());
61479                         if (!view.intersects(extent)) return false;
61480                     }
61481
61482                     return true;
61483                 });
61484             };
61485
61486             validator.getResolvedIssues = function() {
61487                 var baseIssues = Object.values(_baseCache.issuesByIssueID);
61488                 return baseIssues.filter(function(issue) {
61489                     return !_headCache.issuesByIssueID[issue.id];
61490                 });
61491             };
61492
61493             validator.focusIssue = function(issue) {
61494                 var extent = issue.extent(context.graph());
61495
61496                 if (extent) {
61497                     var setZoom = Math.max(context.map().zoom(), 19);
61498                     context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
61499
61500                     // select the first entity
61501                     if (issue.entityIds && issue.entityIds.length) {
61502                         window.setTimeout(function() {
61503                             var ids = issue.entityIds;
61504                             context.enter(modeSelect(context, [ids[0]]));
61505                             dispatch$1.call('focusedIssue', this, issue);
61506                         }, 250);  // after ease
61507                     }
61508                 }
61509             };
61510
61511
61512             validator.getIssuesBySeverity = function(options) {
61513                 var groups = utilArrayGroupBy(validator.getIssues(options), 'severity');
61514                 groups.error = groups.error || [];
61515                 groups.warning = groups.warning || [];
61516                 return groups;
61517             };
61518
61519             // show some issue types in a particular order
61520             var orderedIssueTypes = [
61521                 // flag missing data first
61522                 'missing_tag', 'missing_role',
61523                 // then flag identity issues
61524                 'outdated_tags', 'mismatched_geometry',
61525                 // flag geometry issues where fixing them might solve connectivity issues
61526                 'crossing_ways', 'almost_junction',
61527                 // then flag connectivity issues
61528                 'disconnected_way', 'impossible_oneway'
61529             ];
61530
61531             // returns the issues that the given entity IDs have in common, matching the given options
61532             validator.getSharedEntityIssues = function(entityIDs, options) {
61533                 var cache = _headCache;
61534
61535                 // gather the issues that are common to all the entities
61536                 var issueIDs = entityIDs.reduce(function(acc, entityID) {
61537                     var entityIssueIDs = cache.issuesByEntityID[entityID] || new Set();
61538                     if (!acc) {
61539                         return new Set(entityIssueIDs);
61540                     }
61541                     return new Set([...acc].filter(function(elem) {
61542                         return entityIssueIDs.has(elem);
61543                     }));
61544                 }, null) || [];
61545
61546                 var opts = options || {};
61547
61548                 return Array.from(issueIDs)
61549                     .map(function(id) { return cache.issuesByIssueID[id]; })
61550                     .filter(function(issue) {
61551                         if (!issue) return false;
61552                         if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false;
61553                         if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false;
61554
61555                         if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) return false;
61556                         if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) return false;
61557
61558                         return true;
61559                     }).sort(function(issue1, issue2) {
61560                         if (issue1.type === issue2.type) {
61561                             // issues of the same type, sort deterministically
61562                             return issue1.id < issue2.id ? -1 : 1;
61563                         }
61564                         var index1 = orderedIssueTypes.indexOf(issue1.type);
61565                         var index2 = orderedIssueTypes.indexOf(issue2.type);
61566                         if (index1 !== -1 && index2 !== -1) {
61567                             // both issue types have explicit sort orders
61568                             return index1 - index2;
61569                         } else if (index1 === -1 && index2 === -1) {
61570                             // neither issue type has an explicit sort order, sort by type
61571                             return issue1.type < issue2.type ? -1 : 1;
61572                         } else {
61573                             // order explicit types before everything else
61574                             return index1 !== -1 ? -1 : 1;
61575                         }
61576                     });
61577             };
61578
61579
61580             validator.getEntityIssues = function(entityID, options) {
61581                 return validator.getSharedEntityIssues([entityID], options);
61582             };
61583
61584
61585             validator.getRuleKeys = function() {
61586                 return Object.keys(_rules);
61587             };
61588
61589
61590             validator.isRuleEnabled = function(key) {
61591                 return !_disabledRules[key];
61592             };
61593
61594
61595             validator.toggleRule = function(key) {
61596                 if (_disabledRules[key]) {
61597                     delete _disabledRules[key];
61598                 } else {
61599                     _disabledRules[key] = true;
61600                 }
61601
61602                 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
61603                 validator.validate();
61604             };
61605
61606
61607             validator.disableRules = function(keys) {
61608                 _disabledRules = {};
61609                 keys.forEach(function(k) {
61610                     _disabledRules[k] = true;
61611                 });
61612
61613                 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
61614                 validator.validate();
61615             };
61616
61617
61618             validator.ignoreIssue = function(id) {
61619                 _ignoredIssueIDs[id] = true;
61620             };
61621
61622
61623             //
61624             // Run validation on a single entity for the given graph
61625             //
61626             function validateEntity(entity, graph) {
61627                 var entityIssues = [];
61628
61629                 // runs validation and appends resulting issues
61630                 function runValidation(key) {
61631
61632                     var fn = _rules[key];
61633                     if (typeof fn !== 'function') {
61634                         console.error('no such validation rule = ' + key);  // eslint-disable-line no-console
61635                         return;
61636                     }
61637
61638                     var detected = fn(entity, graph);
61639                     entityIssues = entityIssues.concat(detected);
61640                 }
61641
61642                 // run all rules
61643                 Object.keys(_rules).forEach(runValidation);
61644
61645                 return entityIssues;
61646             }
61647
61648             function entityIDsToValidate(entityIDs, graph) {
61649                 var processedIDs = new Set();
61650                 return entityIDs.reduce(function(acc, entityID) {
61651                     // keep redundancy check separate from `acc` because an `entityID`
61652                     // could have been added to `acc` as a related entity through an earlier pass
61653                     if (processedIDs.has(entityID)) return acc;
61654                     processedIDs.add(entityID);
61655
61656                     var entity = graph.hasEntity(entityID);
61657                     if (!entity) return acc;
61658
61659                     acc.add(entityID);
61660
61661                     var checkParentRels = [entity];
61662
61663                     if (entity.type === 'node') {
61664                         graph.parentWays(entity).forEach(function(parentWay) {
61665                             acc.add(parentWay.id); // include parent ways
61666                             checkParentRels.push(parentWay);
61667                         });
61668                     } else if (entity.type === 'relation') {
61669                         entity.members.forEach(function(member) {
61670                             acc.add(member.id); // include members
61671                         });
61672                     } else if (entity.type === 'way') {
61673                         entity.nodes.forEach(function(nodeID) {
61674                             acc.add(nodeID); // include child nodes
61675                             graph._parentWays[nodeID].forEach(function(wayID) {
61676                                 acc.add(wayID); // include connected ways
61677                             });
61678                         });
61679                     }
61680
61681                     checkParentRels.forEach(function(entity) {   // include parent relations
61682                         if (entity.type !== 'relation') {        // but not super-relations
61683                             graph.parentRelations(entity).forEach(function(parentRelation) {
61684                                 acc.add(parentRelation.id);
61685                             });
61686                         }
61687                     });
61688
61689                     return acc;
61690
61691                 }, new Set());
61692             }
61693
61694             //
61695             // Run validation for several entities, supplied `entityIDs`,
61696             // against `graph` for the given `cache`
61697             //
61698             function validateEntities(entityIDs, graph, cache) {
61699
61700                 // clear caches for existing issues related to these entities
61701                 entityIDs.forEach(cache.uncacheEntityID);
61702
61703                 // detect new issues and update caches
61704                 entityIDs.forEach(function(entityID) {
61705                     var entity = graph.hasEntity(entityID);
61706                     // don't validate deleted entities
61707                     if (!entity) return;
61708
61709                     var issues = validateEntity(entity, graph);
61710                     cache.cacheIssues(issues);
61711                 });
61712             }
61713
61714
61715             //
61716             // Validates anything that has changed since the last time it was run.
61717             // Also updates the "validatedGraph" to be the current graph
61718             // and dispatches a `validated` event when finished.
61719             //
61720             validator.validate = function() {
61721
61722                 var currGraph = context.graph();
61723                 _validatedGraph = _validatedGraph || context.history().base();
61724                 if (currGraph === _validatedGraph) {
61725                     dispatch$1.call('validated');
61726                     return;
61727                 }
61728                 var oldGraph = _validatedGraph;
61729                 var difference = coreDifference(oldGraph, currGraph);
61730                 _validatedGraph = currGraph;
61731
61732                 var createdAndModifiedEntityIDs = difference.extantIDs(true);   // created/modified (true = w/relation members)
61733                 var entityIDsToCheck = entityIDsToValidate(createdAndModifiedEntityIDs, currGraph);
61734
61735                 // check modified and deleted entities against the old graph in order to update their related entities
61736                 // (e.g. deleting the only highway connected to a road should create a disconnected highway issue)
61737                 var modifiedAndDeletedEntityIDs = difference.deleted().concat(difference.modified())
61738                     .map(function(entity) { return entity.id; });
61739                 var entityIDsToCheckForOldGraph = entityIDsToValidate(modifiedAndDeletedEntityIDs, oldGraph);
61740
61741                 // concat the sets
61742                 entityIDsToCheckForOldGraph.forEach(entityIDsToCheck.add, entityIDsToCheck);
61743
61744                 validateEntities(entityIDsToCheck, context.graph(), _headCache);
61745
61746                 dispatch$1.call('validated');
61747             };
61748
61749
61750             // WHEN TO RUN VALIDATION:
61751             // When graph changes:
61752             context.history()
61753                 .on('restore.validator', validator.validate)   // restore saved history
61754                 .on('undone.validator', validator.validate)    // undo
61755                 .on('redone.validator', validator.validate);   // redo
61756                 // but not on 'change' (e.g. while drawing)
61757
61758             // When user chages editing modes:
61759             context
61760                 .on('exit.validator', validator.validate);
61761
61762             // When merging fetched data:
61763             context.history()
61764                 .on('merge.validator', function(entities) {
61765                     if (!entities) return;
61766                     var handle = window.requestIdleCallback(function() {
61767                         var entityIDs = entities.map(function(entity) { return entity.id; });
61768                         var headGraph = context.graph();
61769                         validateEntities(entityIDsToValidate(entityIDs, headGraph), headGraph, _headCache);
61770
61771                         var baseGraph = context.history().base();
61772                         validateEntities(entityIDsToValidate(entityIDs, baseGraph), baseGraph, _baseCache);
61773
61774                         dispatch$1.call('validated');
61775                     });
61776                     _deferred.add(handle);
61777                 });
61778
61779
61780             return validator;
61781         }
61782
61783
61784         function validationCache() {
61785
61786             var cache = {
61787                 issuesByIssueID: {},  // issue.id -> issue
61788                 issuesByEntityID: {} // entity.id -> set(issue.id)
61789             };
61790
61791             cache.cacheIssues = function(issues) {
61792                 issues.forEach(function(issue) {
61793                     var entityIds = issue.entityIds || [];
61794                     entityIds.forEach(function(entityId) {
61795                         if (!cache.issuesByEntityID[entityId]) {
61796                             cache.issuesByEntityID[entityId] = new Set();
61797                         }
61798                         cache.issuesByEntityID[entityId].add(issue.id);
61799                     });
61800                     cache.issuesByIssueID[issue.id] = issue;
61801                 });
61802             };
61803
61804             cache.uncacheIssue = function(issue) {
61805                 // When multiple entities are involved (e.g. crossing_ways),
61806                 // remove this issue from the other entity caches too..
61807                 var entityIds = issue.entityIds || [];
61808                 entityIds.forEach(function(entityId) {
61809                     if (cache.issuesByEntityID[entityId]) {
61810                         cache.issuesByEntityID[entityId].delete(issue.id);
61811                     }
61812                 });
61813                 delete cache.issuesByIssueID[issue.id];
61814             };
61815
61816             cache.uncacheIssues = function(issues) {
61817                 issues.forEach(cache.uncacheIssue);
61818             };
61819
61820             cache.uncacheIssuesOfType = function(type) {
61821                 var issuesOfType = Object.values(cache.issuesByIssueID)
61822                     .filter(function(issue) { return issue.type === type; });
61823                 cache.uncacheIssues(issuesOfType);
61824             };
61825
61826             //
61827             // Remove a single entity and all its related issues from the caches
61828             //
61829             cache.uncacheEntityID = function(entityID) {
61830                 var issueIDs = cache.issuesByEntityID[entityID];
61831                 if (!issueIDs) return;
61832
61833                 issueIDs.forEach(function(issueID) {
61834                     var issue = cache.issuesByIssueID[issueID];
61835                     if (issue) {
61836                         cache.uncacheIssue(issue);
61837                     } else {
61838                         delete cache.issuesByIssueID[issueID];
61839                     }
61840                 });
61841
61842                 delete cache.issuesByEntityID[entityID];
61843             };
61844
61845             return cache;
61846         }
61847
61848         function coreUploader(context) {
61849
61850             var dispatch$1 = dispatch(
61851                 // Start and end events are dispatched exactly once each per legitimate outside call to `save`
61852                 'saveStarted', // dispatched as soon as a call to `save` has been deemed legitimate
61853                 'saveEnded',   // dispatched after the result event has been dispatched
61854
61855                 'willAttemptUpload', // dispatched before the actual upload call occurs, if it will
61856                 'progressChanged',
61857
61858                 // Each save results in one of these outcomes:
61859                 'resultNoChanges', // upload wasn't attempted since there were no edits
61860                 'resultErrors',    // upload failed due to errors
61861                 'resultConflicts', // upload failed due to data conflicts
61862                 'resultSuccess'    // upload completed without errors
61863             );
61864
61865             var _isSaving = false;
61866
61867             var _conflicts = [];
61868             var _errors = [];
61869             var _origChanges;
61870
61871             var _discardTags = {};
61872             _mainFileFetcher.get('discarded')
61873                 .then(function(d) { _discardTags = d; })
61874                 .catch(function() { /* ignore */ });
61875
61876             var uploader = utilRebind({}, dispatch$1, 'on');
61877
61878             uploader.isSaving = function() {
61879                 return _isSaving;
61880             };
61881
61882             uploader.save = function(changeset, tryAgain, checkConflicts) {
61883                 // Guard against accidentally entering save code twice - #4641
61884                 if (_isSaving && !tryAgain) {
61885                     return;
61886                 }
61887
61888                 var osm = context.connection();
61889                 if (!osm) return;
61890
61891                 // If user somehow got logged out mid-save, try to reauthenticate..
61892                 // This can happen if they were logged in from before, but the tokens are no longer valid.
61893                 if (!osm.authenticated()) {
61894                     osm.authenticate(function(err) {
61895                         if (!err) {
61896                             uploader.save(changeset, tryAgain, checkConflicts);  // continue where we left off..
61897                         }
61898                     });
61899                     return;
61900                 }
61901
61902                 if (!_isSaving) {
61903                     _isSaving = true;
61904                     dispatch$1.call('saveStarted', this);
61905                 }
61906
61907                 var history = context.history();
61908
61909                 _conflicts = [];
61910                 _errors = [];
61911
61912                 // Store original changes, in case user wants to download them as an .osc file
61913                 _origChanges = history.changes(actionDiscardTags(history.difference(), _discardTags));
61914
61915                 // First time, `history.perform` a no-op action.
61916                 // Any conflict resolutions will be done as `history.replace`
61917                 // Remember to pop this later if needed
61918                 if (!tryAgain) {
61919                     history.perform(actionNoop());
61920                 }
61921
61922                 // Attempt a fast upload.. If there are conflicts, re-enter with `checkConflicts = true`
61923                 if (!checkConflicts) {
61924                     upload(changeset);
61925
61926                 // Do the full (slow) conflict check..
61927                 } else {
61928                     performFullConflictCheck(changeset);
61929                 }
61930
61931             };
61932
61933
61934             function performFullConflictCheck(changeset) {
61935
61936                 var osm = context.connection();
61937                 if (!osm) return;
61938
61939                 var history = context.history();
61940
61941                 var localGraph = context.graph();
61942                 var remoteGraph = coreGraph(history.base(), true);
61943
61944                 var summary = history.difference().summary();
61945                 var _toCheck = [];
61946                 for (var i = 0; i < summary.length; i++) {
61947                     var item = summary[i];
61948                     if (item.changeType === 'modified') {
61949                         _toCheck.push(item.entity.id);
61950                     }
61951                 }
61952
61953                 var _toLoad = withChildNodes(_toCheck, localGraph);
61954                 var _loaded = {};
61955                 var _toLoadCount = 0;
61956                 var _toLoadTotal = _toLoad.length;
61957
61958                 if (_toCheck.length) {
61959                     dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
61960                     _toLoad.forEach(function(id) { _loaded[id] = false; });
61961                     osm.loadMultiple(_toLoad, loaded);
61962                 } else {
61963                     upload(changeset);
61964                 }
61965
61966                 return;
61967
61968                 function withChildNodes(ids, graph) {
61969                     var s = new Set(ids);
61970                     ids.forEach(function(id) {
61971                         var entity = graph.entity(id);
61972                         if (entity.type !== 'way') return;
61973
61974                         graph.childNodes(entity).forEach(function(child) {
61975                             if (child.version !== undefined) {
61976                                 s.add(child.id);
61977                             }
61978                         });
61979                     });
61980
61981                     return Array.from(s);
61982                 }
61983
61984
61985                 // Reload modified entities into an alternate graph and check for conflicts..
61986                 function loaded(err, result) {
61987                     if (_errors.length) return;
61988
61989                     if (err) {
61990                         _errors.push({
61991                             msg: err.message || err.responseText,
61992                             details: [ _t('save.status_code', { code: err.status }) ]
61993                         });
61994                         didResultInErrors();
61995
61996                     } else {
61997                         var loadMore = [];
61998
61999                         result.data.forEach(function(entity) {
62000                             remoteGraph.replace(entity);
62001                             _loaded[entity.id] = true;
62002                             _toLoad = _toLoad.filter(function(val) { return val !== entity.id; });
62003
62004                             if (!entity.visible) return;
62005
62006                             // Because loadMultiple doesn't download /full like loadEntity,
62007                             // need to also load children that aren't already being checked..
62008                             var i, id;
62009                             if (entity.type === 'way') {
62010                                 for (i = 0; i < entity.nodes.length; i++) {
62011                                     id = entity.nodes[i];
62012                                     if (_loaded[id] === undefined) {
62013                                         _loaded[id] = false;
62014                                         loadMore.push(id);
62015                                     }
62016                                 }
62017                             } else if (entity.type === 'relation' && entity.isMultipolygon()) {
62018                                 for (i = 0; i < entity.members.length; i++) {
62019                                     id = entity.members[i].id;
62020                                     if (_loaded[id] === undefined) {
62021                                         _loaded[id] = false;
62022                                         loadMore.push(id);
62023                                     }
62024                                 }
62025                             }
62026                         });
62027
62028                         _toLoadCount += result.data.length;
62029                         _toLoadTotal += loadMore.length;
62030                         dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
62031
62032                         if (loadMore.length) {
62033                             _toLoad.push.apply(_toLoad, loadMore);
62034                             osm.loadMultiple(loadMore, loaded);
62035                         }
62036
62037                         if (!_toLoad.length) {
62038                             detectConflicts();
62039                             upload(changeset);
62040                         }
62041                     }
62042                 }
62043
62044
62045                 function detectConflicts() {
62046                     function choice(id, text, action) {
62047                         return {
62048                             id: id,
62049                             text: text,
62050                             action: function() {
62051                                 history.replace(action);
62052                             }
62053                         };
62054                     }
62055                     function formatUser(d) {
62056                         return '<a href="' + osm.userURL(d) + '" target="_blank">' + d + '</a>';
62057                     }
62058                     function entityName(entity) {
62059                         return utilDisplayName(entity) || (utilDisplayType(entity.id) + ' ' + entity.id);
62060                     }
62061
62062                     function sameVersions(local, remote) {
62063                         if (local.version !== remote.version) return false;
62064
62065                         if (local.type === 'way') {
62066                             var children = utilArrayUnion(local.nodes, remote.nodes);
62067                             for (var i = 0; i < children.length; i++) {
62068                                 var a = localGraph.hasEntity(children[i]);
62069                                 var b = remoteGraph.hasEntity(children[i]);
62070                                 if (a && b && a.version !== b.version) return false;
62071                             }
62072                         }
62073
62074                         return true;
62075                     }
62076
62077                     _toCheck.forEach(function(id) {
62078                         var local = localGraph.entity(id);
62079                         var remote = remoteGraph.entity(id);
62080
62081                         if (sameVersions(local, remote)) return;
62082
62083                         var merge = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags, formatUser);
62084
62085                         history.replace(merge);
62086
62087                         var mergeConflicts = merge.conflicts();
62088                         if (!mergeConflicts.length) return;  // merged safely
62089
62090                         var forceLocal = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_local');
62091                         var forceRemote = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_remote');
62092                         var keepMine = _t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore'));
62093                         var keepTheirs = _t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
62094
62095                         _conflicts.push({
62096                             id: id,
62097                             name: entityName(local),
62098                             details: mergeConflicts,
62099                             chosen: 1,
62100                             choices: [
62101                                 choice(id, keepMine, forceLocal),
62102                                 choice(id, keepTheirs, forceRemote)
62103                             ]
62104                         });
62105                     });
62106                 }
62107             }
62108
62109
62110             function upload(changeset) {
62111                 var osm = context.connection();
62112                 if (!osm) {
62113                     _errors.push({ msg: 'No OSM Service' });
62114                 }
62115
62116                 if (_conflicts.length) {
62117                     didResultInConflicts(changeset);
62118
62119                 } else if (_errors.length) {
62120                     didResultInErrors();
62121
62122                 } else {
62123                     var history = context.history();
62124                     var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
62125                     if (changes.modified.length || changes.created.length || changes.deleted.length) {
62126
62127                         dispatch$1.call('willAttemptUpload', this);
62128
62129                         osm.putChangeset(changeset, changes, uploadCallback);
62130
62131                     } else {
62132                         // changes were insignificant or reverted by user
62133                         didResultInNoChanges();
62134                     }
62135                 }
62136             }
62137
62138
62139             function uploadCallback(err, changeset) {
62140                 if (err) {
62141                     if (err.status === 409) {  // 409 Conflict
62142                         uploader.save(changeset, true, true);  // tryAgain = true, checkConflicts = true
62143                     } else {
62144                         _errors.push({
62145                             msg: err.message || err.responseText,
62146                             details: [ _t('save.status_code', { code: err.status }) ]
62147                         });
62148                         didResultInErrors();
62149                     }
62150
62151                 } else {
62152                     didResultInSuccess(changeset);
62153                 }
62154             }
62155
62156             function didResultInNoChanges() {
62157
62158                 dispatch$1.call('resultNoChanges', this);
62159
62160                 endSave();
62161
62162                 context.flush(); // reset iD
62163             }
62164
62165             function didResultInErrors() {
62166
62167                 context.history().pop();
62168
62169                 dispatch$1.call('resultErrors', this, _errors);
62170
62171                 endSave();
62172             }
62173
62174
62175             function didResultInConflicts(changeset) {
62176
62177                 _conflicts.sort(function(a, b) { return b.id.localeCompare(a.id); });
62178
62179                 dispatch$1.call('resultConflicts', this, changeset, _conflicts, _origChanges);
62180
62181                 endSave();
62182             }
62183
62184
62185             function didResultInSuccess(changeset) {
62186
62187                 // delete the edit stack cached to local storage
62188                 context.history().clearSaved();
62189
62190                 dispatch$1.call('resultSuccess', this, changeset);
62191
62192                 // Add delay to allow for postgres replication #1646 #2678
62193                 window.setTimeout(function() {
62194
62195                     endSave();
62196
62197                     context.flush(); // reset iD
62198                 }, 2500);
62199             }
62200
62201
62202             function endSave() {
62203                 _isSaving = false;
62204
62205                 dispatch$1.call('saveEnded', this);
62206             }
62207
62208
62209             uploader.cancelConflictResolution = function() {
62210                 context.history().pop();
62211             };
62212
62213
62214             uploader.processResolvedConflicts = function(changeset) {
62215                 var history = context.history();
62216
62217                 for (var i = 0; i < _conflicts.length; i++) {
62218                     if (_conflicts[i].chosen === 1) {  // user chose "use theirs"
62219                         var entity = context.hasEntity(_conflicts[i].id);
62220                         if (entity && entity.type === 'way') {
62221                             var children = utilArrayUniq(entity.nodes);
62222                             for (var j = 0; j < children.length; j++) {
62223                                 history.replace(actionRevert(children[j]));
62224                             }
62225                         }
62226                         history.replace(actionRevert(_conflicts[i].id));
62227                     }
62228                 }
62229
62230                 uploader.save(changeset, true, false);  // tryAgain = true, checkConflicts = false
62231             };
62232
62233
62234             uploader.reset = function() {
62235
62236             };
62237
62238
62239             return uploader;
62240         }
62241
62242         var isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
62243
62244         // listen for DPI change, e.g. when dragging a browser window from a retina to non-retina screen
62245         window.matchMedia(`
62246         (-webkit-min-device-pixel-ratio: 2), /* Safari */
62247         (min-resolution: 2dppx),             /* standard */
62248         (min-resolution: 192dpi)             /* fallback */
62249     `).addListener(function() {
62250
62251             isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
62252         });
62253
62254
62255         function localeDateString(s) {
62256             if (!s) return null;
62257             var options = { day: 'numeric', month: 'short', year: 'numeric' };
62258             var d = new Date(s);
62259             if (isNaN(d.getTime())) return null;
62260             return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
62261         }
62262
62263         function vintageRange(vintage) {
62264             var s;
62265             if (vintage.start || vintage.end) {
62266                 s = (vintage.start || '?');
62267                 if (vintage.start !== vintage.end) {
62268                     s += ' - ' + (vintage.end || '?');
62269                 }
62270             }
62271             return s;
62272         }
62273
62274
62275         function rendererBackgroundSource(data) {
62276             var source = Object.assign({}, data);   // shallow copy
62277             var _offset = [0, 0];
62278             var _name = source.name;
62279             var _description = source.description;
62280             var _best = !!source.best;
62281             var _template = source.encrypted ? utilAesDecrypt(source.template) : source.template;
62282
62283             source.tileSize = data.tileSize || 256;
62284             source.zoomExtent = data.zoomExtent || [0, 22];
62285             source.overzoom = data.overzoom !== false;
62286
62287             source.offset = function(val) {
62288                 if (!arguments.length) return _offset;
62289                 _offset = val;
62290                 return source;
62291             };
62292
62293
62294             source.nudge = function(val, zoomlevel) {
62295                 _offset[0] += val[0] / Math.pow(2, zoomlevel);
62296                 _offset[1] += val[1] / Math.pow(2, zoomlevel);
62297                 return source;
62298             };
62299
62300
62301             source.name = function() {
62302                 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
62303                 return _t('imagery.' + id_safe + '.name', { default: _name });
62304             };
62305
62306
62307             source.description = function() {
62308                 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
62309                 return _t('imagery.' + id_safe + '.description', { default: _description });
62310             };
62311
62312
62313             source.best = function() {
62314                 return _best;
62315             };
62316
62317
62318             source.area = function() {
62319                 if (!data.polygon) return Number.MAX_VALUE;  // worldwide
62320                 var area = d3_geoArea({ type: 'MultiPolygon', coordinates: [ data.polygon ] });
62321                 return isNaN(area) ? 0 : area;
62322             };
62323
62324
62325             source.imageryUsed = function() {
62326                 return name || source.id;
62327             };
62328
62329
62330             source.template = function(val) {
62331                 if (!arguments.length) return _template;
62332                 if (source.id === 'custom') {
62333                     _template = val;
62334                 }
62335                 return source;
62336             };
62337
62338
62339             source.url = function(coord) {
62340                 var result = _template;
62341                 if (result === '') return result;   // source 'none'
62342
62343
62344                 // Guess a type based on the tokens present in the template
62345                 // (This is for 'custom' source, where we don't know)
62346                 if (!source.type) {
62347                     if (/\{(proj|wkid|bbox)\}/.test(_template)) {
62348                         source.type = 'wms';
62349                         source.projection = 'EPSG:3857';  // guess
62350                     } else if (/\{(x|y)\}/.test(_template)) {
62351                         source.type = 'tms';
62352                     } else if (/\{u\}/.test(_template)) {
62353                         source.type = 'bing';
62354                     }
62355                 }
62356
62357
62358                 if (source.type === 'wms') {
62359                     var tileToProjectedCoords = (function(x, y, z) {
62360                         //polyfill for IE11, PhantomJS
62361                         var sinh = Math.sinh || function(x) {
62362                             var y = Math.exp(x);
62363                             return (y - 1 / y) / 2;
62364                         };
62365
62366                         var zoomSize = Math.pow(2, z);
62367                         var lon = x / zoomSize * Math.PI * 2 - Math.PI;
62368                         var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize)));
62369
62370                         switch (source.projection) {
62371                             case 'EPSG:4326':
62372                                 return {
62373                                     x: lon * 180 / Math.PI,
62374                                     y: lat * 180 / Math.PI
62375                                 };
62376                             default: // EPSG:3857 and synonyms
62377                                 var mercCoords = mercatorRaw(lon, lat);
62378                                 return {
62379                                     x: 20037508.34 / Math.PI * mercCoords[0],
62380                                     y: 20037508.34 / Math.PI * mercCoords[1]
62381                                 };
62382                         }
62383                     });
62384
62385                     var tileSize = source.tileSize;
62386                     var projection = source.projection;
62387                     var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]);
62388                     var maxXminY = tileToProjectedCoords(coord[0]+1, coord[1]+1, coord[2]);
62389
62390                     result = result.replace(/\{(\w+)\}/g, function (token, key) {
62391                       switch (key) {
62392                         case 'width':
62393                         case 'height':
62394                           return tileSize;
62395                         case 'proj':
62396                           return projection;
62397                         case 'wkid':
62398                           return projection.replace(/^EPSG:/, '');
62399                         case 'bbox':
62400                           return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y;
62401                         case 'w':
62402                           return minXmaxY.x;
62403                         case 's':
62404                           return maxXminY.y;
62405                         case 'n':
62406                           return maxXminY.x;
62407                         case 'e':
62408                           return minXmaxY.y;
62409                         default:
62410                           return token;
62411                       }
62412                     });
62413
62414                 } else if (source.type === 'tms') {
62415                     result = result
62416                         .replace('{x}', coord[0])
62417                         .replace('{y}', coord[1])
62418                         // TMS-flipped y coordinate
62419                         .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1)
62420                         .replace(/\{z(oom)?\}/, coord[2])
62421                         // only fetch retina tiles for retina screens
62422                         .replace(/\{@2x\}|\{r\}/, isRetina ? '@2x' : '');
62423
62424                 } else if (source.type === 'bing') {
62425                     result = result
62426                         .replace('{u}', function() {
62427                             var u = '';
62428                             for (var zoom = coord[2]; zoom > 0; zoom--) {
62429                                 var b = 0;
62430                                 var mask = 1 << (zoom - 1);
62431                                 if ((coord[0] & mask) !== 0) b++;
62432                                 if ((coord[1] & mask) !== 0) b += 2;
62433                                 u += b.toString();
62434                             }
62435                             return u;
62436                         });
62437                 }
62438
62439                 // these apply to any type..
62440                 result = result.replace(/\{switch:([^}]+)\}/, function(s, r) {
62441                     var subdomains = r.split(',');
62442                     return subdomains[(coord[0] + coord[1]) % subdomains.length];
62443                 });
62444
62445
62446                 return result;
62447             };
62448
62449
62450             source.validZoom = function(z) {
62451                 return source.zoomExtent[0] <= z &&
62452                     (source.overzoom || source.zoomExtent[1] > z);
62453             };
62454
62455
62456             source.isLocatorOverlay = function() {
62457                 return source.id === 'mapbox_locator_overlay';
62458             };
62459
62460
62461             /* hides a source from the list, but leaves it available for use */
62462             source.isHidden = function() {
62463                 return source.id === 'DigitalGlobe-Premium-vintage' ||
62464                     source.id === 'DigitalGlobe-Standard-vintage';
62465             };
62466
62467
62468             source.copyrightNotices = function() {};
62469
62470
62471             source.getMetadata = function(center, tileCoord, callback) {
62472                 var vintage = {
62473                     start: localeDateString(source.startDate),
62474                     end: localeDateString(source.endDate)
62475                 };
62476                 vintage.range = vintageRange(vintage);
62477
62478                 var metadata = { vintage: vintage };
62479                 callback(null, metadata);
62480             };
62481
62482
62483             return source;
62484         }
62485
62486
62487         rendererBackgroundSource.Bing = function(data, dispatch) {
62488             // http://msdn.microsoft.com/en-us/library/ff701716.aspx
62489             // http://msdn.microsoft.com/en-us/library/ff701701.aspx
62490
62491             data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
62492
62493             var bing = rendererBackgroundSource(data);
62494             // var key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU'; // P2, JOSM, etc
62495             var key = 'Ak5oTE46TUbjRp08OFVcGpkARErDobfpuyNKa-W2mQ8wbt1K1KL8p1bIRwWwcF-Q';    // iD
62496
62497
62498             var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' + key;
62499             var cache = {};
62500             var inflight = {};
62501             var providers = [];
62502
62503             d3_json(url)
62504                 .then(function(json) {
62505                     providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) {
62506                         return {
62507                             attribution: provider.attribution,
62508                             areas: provider.coverageAreas.map(function(area) {
62509                                 return {
62510                                     zoom: [area.zoomMin, area.zoomMax],
62511                                     extent: geoExtent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
62512                                 };
62513                             })
62514                         };
62515                     });
62516                     dispatch.call('change');
62517                 })
62518                 .catch(function() {
62519                     /* ignore */
62520                 });
62521
62522
62523             bing.copyrightNotices = function(zoom, extent) {
62524                 zoom = Math.min(zoom, 21);
62525                 return providers.filter(function(provider) {
62526                     return provider.areas.some(function(area) {
62527                         return extent.intersects(area.extent) &&
62528                             area.zoom[0] <= zoom &&
62529                             area.zoom[1] >= zoom;
62530                     });
62531                 }).map(function(provider) {
62532                     return provider.attribution;
62533                 }).join(', ');
62534             };
62535
62536
62537             bing.getMetadata = function(center, tileCoord, callback) {
62538                 var tileID = tileCoord.slice(0, 3).join('/');
62539                 var zoom = Math.min(tileCoord[2], 21);
62540                 var centerPoint = center[1] + ',' + center[0];  // lat,lng
62541                 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint +
62542                         '?zl=' + zoom + '&key=' + key;
62543
62544                 if (inflight[tileID]) return;
62545
62546                 if (!cache[tileID]) {
62547                     cache[tileID] = {};
62548                 }
62549                 if (cache[tileID] && cache[tileID].metadata) {
62550                     return callback(null, cache[tileID].metadata);
62551                 }
62552
62553                 inflight[tileID] = true;
62554                 d3_json(url)
62555                     .then(function(result) {
62556                         delete inflight[tileID];
62557                         if (!result) {
62558                             throw new Error('Unknown Error');
62559                         }
62560                         var vintage = {
62561                             start: localeDateString(result.resourceSets[0].resources[0].vintageStart),
62562                             end: localeDateString(result.resourceSets[0].resources[0].vintageEnd)
62563                         };
62564                         vintage.range = vintageRange(vintage);
62565
62566                         var metadata = { vintage: vintage };
62567                         cache[tileID].metadata = metadata;
62568                         if (callback) callback(null, metadata);
62569                     })
62570                     .catch(function(err) {
62571                         delete inflight[tileID];
62572                         if (callback) callback(err.message);
62573                     });
62574             };
62575
62576
62577             bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
62578
62579
62580             return bing;
62581         };
62582
62583
62584
62585         rendererBackgroundSource.Esri = function(data) {
62586             // in addition to using the tilemap at zoom level 20, overzoom real tiles - #4327 (deprecated technique, but it works)
62587             if (data.template.match(/blankTile/) === null) {
62588                 data.template = data.template + '?blankTile=false';
62589             }
62590
62591             var esri = rendererBackgroundSource(data);
62592             var cache = {};
62593             var inflight = {};
62594             var _prevCenter;
62595
62596             // use a tilemap service to set maximum zoom for esri tiles dynamically
62597             // https://developers.arcgis.com/documentation/tiled-elevation-service/
62598             esri.fetchTilemap = function(center) {
62599                 // skip if we have already fetched a tilemap within 5km
62600                 if (_prevCenter && geoSphericalDistance(center, _prevCenter) < 5000) return;
62601                 _prevCenter = center;
62602
62603                 // tiles are available globally to zoom level 19, afterward they may or may not be present
62604                 var z = 20;
62605
62606                 // first generate a random url using the template
62607                 var dummyUrl = esri.url([1,2,3]);
62608
62609                 // calculate url z/y/x from the lat/long of the center of the map
62610                 var x = (Math.floor((center[0] + 180) / 360 * Math.pow(2, z)));
62611                 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)));
62612
62613                 // fetch an 8x8 grid to leverage cache
62614                 var tilemapUrl = dummyUrl.replace(/tile\/[0-9]+\/[0-9]+\/[0-9]+\?blankTile=false/, 'tilemap') + '/' + z + '/' + y + '/' + x + '/8/8';
62615
62616                 // make the request and introspect the response from the tilemap server
62617                 d3_json(tilemapUrl)
62618                     .then(function(tilemap) {
62619                         if (!tilemap) {
62620                             throw new Error('Unknown Error');
62621                         }
62622                         var hasTiles = true;
62623                         for (var i = 0; i < tilemap.data.length; i++) {
62624                             // 0 means an individual tile in the grid doesn't exist
62625                             if (!tilemap.data[i]) {
62626                                 hasTiles = false;
62627                                 break;
62628                             }
62629                         }
62630
62631                         // if any tiles are missing at level 20 we restrict maxZoom to 19
62632                         esri.zoomExtent[1] = (hasTiles ? 22 : 19);
62633                     })
62634                     .catch(function() {
62635                         /* ignore */
62636                     });
62637             };
62638
62639
62640             esri.getMetadata = function(center, tileCoord, callback) {
62641                 var tileID = tileCoord.slice(0, 3).join('/');
62642                 var zoom = Math.min(tileCoord[2], esri.zoomExtent[1]);
62643                 var centerPoint = center[0] + ',' + center[1];  // long, lat (as it should be)
62644                 var unknown = _t('info_panels.background.unknown');
62645                 var metadataLayer;
62646                 var vintage = {};
62647                 var metadata = {};
62648
62649                 if (inflight[tileID]) return;
62650
62651                 switch (true) {
62652                     case (zoom >= 20 && esri.id === 'EsriWorldImageryClarity'):
62653                         metadataLayer = 4;
62654                         break;
62655                     case zoom >= 19:
62656                         metadataLayer = 3;
62657                         break;
62658                     case zoom >= 17:
62659                         metadataLayer = 2;
62660                         break;
62661                     case zoom >= 13:
62662                         metadataLayer = 0;
62663                         break;
62664                     default:
62665                         metadataLayer = 99;
62666                 }
62667
62668                 var url;
62669                 // build up query using the layer appropriate to the current zoom
62670                 if (esri.id === 'EsriWorldImagery') {
62671                     url = 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/';
62672                 } else if (esri.id === 'EsriWorldImageryClarity') {
62673                     url = 'https://serviceslab.arcgisonline.com/arcgis/rest/services/Clarity_World_Imagery/MapServer/';
62674                 }
62675
62676                 url += metadataLayer + '/query?returnGeometry=false&geometry=' + centerPoint + '&inSR=4326&geometryType=esriGeometryPoint&outFields=*&f=json';
62677
62678                 if (!cache[tileID]) {
62679                     cache[tileID] = {};
62680                 }
62681                 if (cache[tileID] && cache[tileID].metadata) {
62682                     return callback(null, cache[tileID].metadata);
62683                 }
62684
62685                 // accurate metadata is only available >= 13
62686                 if (metadataLayer === 99) {
62687                     vintage = {
62688                         start: null,
62689                         end: null,
62690                         range: null
62691                     };
62692                     metadata = {
62693                         vintage: null,
62694                         source: unknown,
62695                         description: unknown,
62696                         resolution: unknown,
62697                         accuracy: unknown
62698                     };
62699
62700                     callback(null, metadata);
62701
62702                 } else {
62703                     inflight[tileID] = true;
62704                     d3_json(url)
62705                         .then(function(result) {
62706                             delete inflight[tileID];
62707                             if (!result) {
62708                                 throw new Error('Unknown Error');
62709                             } else if (result.features && result.features.length < 1) {
62710                                 throw new Error('No Results');
62711                             } else if (result.error && result.error.message) {
62712                                 throw new Error(result.error.message);
62713                             }
62714
62715                             // pass through the discrete capture date from metadata
62716                             var captureDate = localeDateString(result.features[0].attributes.SRC_DATE2);
62717                             vintage = {
62718                                 start: captureDate,
62719                                 end: captureDate,
62720                                 range: captureDate
62721                             };
62722                             metadata = {
62723                                 vintage: vintage,
62724                                 source: clean(result.features[0].attributes.NICE_NAME),
62725                                 description: clean(result.features[0].attributes.NICE_DESC),
62726                                 resolution: clean(+parseFloat(result.features[0].attributes.SRC_RES).toFixed(4)),
62727                                 accuracy: clean(+parseFloat(result.features[0].attributes.SRC_ACC).toFixed(4))
62728                             };
62729
62730                             // append units - meters
62731                             if (isFinite(metadata.resolution)) {
62732                                 metadata.resolution += ' m';
62733                             }
62734                             if (isFinite(metadata.accuracy)) {
62735                                 metadata.accuracy += ' m';
62736                             }
62737
62738                             cache[tileID].metadata = metadata;
62739                             if (callback) callback(null, metadata);
62740                         })
62741                         .catch(function(err) {
62742                             delete inflight[tileID];
62743                             if (callback) callback(err.message);
62744                         });
62745                 }
62746
62747
62748                 function clean(val) {
62749                     return String(val).trim() || unknown;
62750                 }
62751             };
62752
62753             return esri;
62754         };
62755
62756
62757         rendererBackgroundSource.None = function() {
62758             var source = rendererBackgroundSource({ id: 'none', template: '' });
62759
62760
62761             source.name = function() {
62762                 return _t('background.none');
62763             };
62764
62765
62766             source.imageryUsed = function() {
62767                 return null;
62768             };
62769
62770
62771             source.area = function() {
62772                 return -1;  // sources in background pane are sorted by area
62773             };
62774
62775
62776             return source;
62777         };
62778
62779
62780         rendererBackgroundSource.Custom = function(template) {
62781             var source = rendererBackgroundSource({ id: 'custom', template: template });
62782
62783
62784             source.name = function() {
62785                 return _t('background.custom');
62786             };
62787
62788
62789             source.imageryUsed = function() {
62790                 // sanitize personal connection tokens - #6801
62791                 var cleaned = source.template();
62792
62793                 // from query string parameters
62794                 if (cleaned.indexOf('?') !== -1) {
62795                     var parts = cleaned.split('?', 2);
62796                     var qs = utilStringQs(parts[1]);
62797
62798                     ['access_token', 'connectId', 'token'].forEach(function(param) {
62799                         if (qs[param]) {
62800                             qs[param] = '{apikey}';
62801                         }
62802                     });
62803                     cleaned = parts[0] + '?' + utilQsString(qs, true);  // true = soft encode
62804                 }
62805
62806                 // from wms/wmts api path parameters
62807                 cleaned = cleaned.replace(/token\/(\w+)/, 'token/{apikey}');
62808
62809                 return 'Custom (' + cleaned + ' )';
62810             };
62811
62812
62813             source.area = function() {
62814                 return -2;  // sources in background pane are sorted by area
62815             };
62816
62817
62818             return source;
62819         };
62820
62821         function rendererTileLayer(context) {
62822             var transformProp = utilPrefixCSSProperty('Transform');
62823             var tiler = utilTiler();
62824
62825             var _tileSize = 256;
62826             var _projection;
62827             var _cache = {};
62828             var _tileOrigin;
62829             var _zoom;
62830             var _source;
62831
62832
62833             function tileSizeAtZoom(d, z) {
62834                 var EPSILON = 0.002;    // close seams
62835                 return ((_tileSize * Math.pow(2, z - d[2])) / _tileSize) + EPSILON;
62836             }
62837
62838
62839             function atZoom(t, distance) {
62840                 var power = Math.pow(2, distance);
62841                 return [
62842                     Math.floor(t[0] * power),
62843                     Math.floor(t[1] * power),
62844                     t[2] + distance
62845                 ];
62846             }
62847
62848
62849             function lookUp(d) {
62850                 for (var up = -1; up > -d[2]; up--) {
62851                     var tile = atZoom(d, up);
62852                     if (_cache[_source.url(tile)] !== false) {
62853                         return tile;
62854                     }
62855                 }
62856             }
62857
62858
62859             function uniqueBy(a, n) {
62860                 var o = [];
62861                 var seen = {};
62862                 for (var i = 0; i < a.length; i++) {
62863                     if (seen[a[i][n]] === undefined) {
62864                         o.push(a[i]);
62865                         seen[a[i][n]] = true;
62866                     }
62867                 }
62868                 return o;
62869             }
62870
62871
62872             function addSource(d) {
62873                 d.push(_source.url(d));
62874                 return d;
62875             }
62876
62877
62878             // Update tiles based on current state of `projection`.
62879             function background(selection) {
62880                 _zoom = geoScaleToZoom(_projection.scale(), _tileSize);
62881
62882                 var pixelOffset;
62883                 if (_source) {
62884                     pixelOffset = [
62885                         _source.offset()[0] * Math.pow(2, _zoom),
62886                         _source.offset()[1] * Math.pow(2, _zoom)
62887                     ];
62888                 } else {
62889                     pixelOffset = [0, 0];
62890                 }
62891
62892                 var translate = [
62893                     _projection.translate()[0] + pixelOffset[0],
62894                     _projection.translate()[1] + pixelOffset[1]
62895                 ];
62896
62897                 tiler
62898                     .scale(_projection.scale() * 2 * Math.PI)
62899                     .translate(translate);
62900
62901                 _tileOrigin = [
62902                     _projection.scale() * Math.PI - translate[0],
62903                     _projection.scale() * Math.PI - translate[1]
62904                 ];
62905
62906                 render(selection);
62907             }
62908
62909
62910             // Derive the tiles onscreen, remove those offscreen and position them.
62911             // Important that this part not depend on `_projection` because it's
62912             // rentered when tiles load/error (see #644).
62913             function render(selection) {
62914                 if (!_source) return;
62915                 var requests = [];
62916                 var showDebug = context.getDebug('tile') && !_source.overlay;
62917
62918                 if (_source.validZoom(_zoom)) {
62919                     tiler.skipNullIsland(!!_source.overlay);
62920
62921                     tiler().forEach(function(d) {
62922                         addSource(d);
62923                         if (d[3] === '') return;
62924                         if (typeof d[3] !== 'string') return; // Workaround for #2295
62925                         requests.push(d);
62926                         if (_cache[d[3]] === false && lookUp(d)) {
62927                             requests.push(addSource(lookUp(d)));
62928                         }
62929                     });
62930
62931                     requests = uniqueBy(requests, 3).filter(function(r) {
62932                         // don't re-request tiles which have failed in the past
62933                         return _cache[r[3]] !== false;
62934                     });
62935                 }
62936
62937                 function load(d) {
62938                     _cache[d[3]] = true;
62939                     select(this)
62940                         .on('error', null)
62941                         .on('load', null)
62942                         .classed('tile-loaded', true);
62943                     render(selection);
62944                 }
62945
62946                 function error(d) {
62947                     _cache[d[3]] = false;
62948                     select(this)
62949                         .on('error', null)
62950                         .on('load', null)
62951                         .remove();
62952                     render(selection);
62953                 }
62954
62955                 function imageTransform(d) {
62956                     var ts = _tileSize * Math.pow(2, _zoom - d[2]);
62957                     var scale = tileSizeAtZoom(d, _zoom);
62958                     return 'translate(' +
62959                         ((d[0] * ts) - _tileOrigin[0]) + 'px,' +
62960                         ((d[1] * ts) - _tileOrigin[1]) + 'px) ' +
62961                         'scale(' + scale + ',' + scale + ')';
62962                 }
62963
62964                 function tileCenter(d) {
62965                     var ts = _tileSize * Math.pow(2, _zoom - d[2]);
62966                     return [
62967                         ((d[0] * ts) - _tileOrigin[0] + (ts / 2)),
62968                         ((d[1] * ts) - _tileOrigin[1] + (ts / 2))
62969                     ];
62970                 }
62971
62972                 function debugTransform(d) {
62973                     var coord = tileCenter(d);
62974                     return 'translate(' + coord[0] + 'px,' + coord[1] + 'px)';
62975                 }
62976
62977
62978                 // Pick a representative tile near the center of the viewport
62979                 // (This is useful for sampling the imagery vintage)
62980                 var dims = tiler.size();
62981                 var mapCenter = [dims[0] / 2, dims[1] / 2];
62982                 var minDist = Math.max(dims[0], dims[1]);
62983                 var nearCenter;
62984
62985                 requests.forEach(function(d) {
62986                     var c = tileCenter(d);
62987                     var dist = geoVecLength(c, mapCenter);
62988                     if (dist < minDist) {
62989                         minDist = dist;
62990                         nearCenter = d;
62991                     }
62992                 });
62993
62994
62995                 var image = selection.selectAll('img')
62996                     .data(requests, function(d) { return d[3]; });
62997
62998                 image.exit()
62999                     .style(transformProp, imageTransform)
63000                     .classed('tile-removing', true)
63001                     .classed('tile-center', false)
63002                     .each(function() {
63003                         var tile = select(this);
63004                         window.setTimeout(function() {
63005                             if (tile.classed('tile-removing')) {
63006                                 tile.remove();
63007                             }
63008                         }, 300);
63009                     });
63010
63011                 image.enter()
63012                   .append('img')
63013                     .attr('class', 'tile')
63014                     .attr('draggable', 'false')
63015                     .style('width', _tileSize + 'px')
63016                     .style('height', _tileSize + 'px')
63017                     .attr('src', function(d) { return d[3]; })
63018                     .on('error', error)
63019                     .on('load', load)
63020                   .merge(image)
63021                     .style(transformProp, imageTransform)
63022                     .classed('tile-debug', showDebug)
63023                     .classed('tile-removing', false)
63024                     .classed('tile-center', function(d) { return d === nearCenter; });
63025
63026
63027
63028                 var debug = selection.selectAll('.tile-label-debug')
63029                     .data(showDebug ? requests : [], function(d) { return d[3]; });
63030
63031                 debug.exit()
63032                     .remove();
63033
63034                 if (showDebug) {
63035                     var debugEnter = debug.enter()
63036                         .append('div')
63037                         .attr('class', 'tile-label-debug');
63038
63039                     debugEnter
63040                         .append('div')
63041                         .attr('class', 'tile-label-debug-coord');
63042
63043                     debugEnter
63044                         .append('div')
63045                         .attr('class', 'tile-label-debug-vintage');
63046
63047                     debug = debug.merge(debugEnter);
63048
63049                     debug
63050                         .style(transformProp, debugTransform);
63051
63052                     debug
63053                         .selectAll('.tile-label-debug-coord')
63054                         .text(function(d) { return d[2] + ' / ' + d[0] + ' / ' + d[1]; });
63055
63056                     debug
63057                         .selectAll('.tile-label-debug-vintage')
63058                         .each(function(d) {
63059                             var span = select(this);
63060                             var center = context.projection.invert(tileCenter(d));
63061                             _source.getMetadata(center, d, function(err, result) {
63062                                 span.text((result && result.vintage && result.vintage.range) ||
63063                                     _t('info_panels.background.vintage') + ': ' + _t('info_panels.background.unknown')
63064                                 );
63065                             });
63066                         });
63067                 }
63068
63069             }
63070
63071
63072             background.projection = function(val) {
63073                 if (!arguments.length) return _projection;
63074                 _projection = val;
63075                 return background;
63076             };
63077
63078
63079             background.dimensions = function(val) {
63080                 if (!arguments.length) return tiler.size();
63081                 tiler.size(val);
63082                 return background;
63083             };
63084
63085
63086             background.source = function(val) {
63087                 if (!arguments.length) return _source;
63088                 _source = val;
63089                 _tileSize = _source.tileSize;
63090                 _cache = {};
63091                 tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent);
63092                 return background;
63093             };
63094
63095
63096             return background;
63097         }
63098
63099         let _imageryIndex = null;
63100
63101         function rendererBackground(context) {
63102           const dispatch$1 = dispatch('change');
63103           const detected = utilDetect();
63104           const baseLayer = rendererTileLayer(context).projection(context.projection);
63105           let _isValid = true;
63106           let _overlayLayers = [];
63107           let _brightness = 1;
63108           let _contrast = 1;
63109           let _saturation = 1;
63110           let _sharpness = 1;
63111
63112
63113           function ensureImageryIndex() {
63114             return _mainFileFetcher.get('imagery')
63115               .then(sources => {
63116                 if (_imageryIndex) return _imageryIndex;
63117
63118                 _imageryIndex = {
63119                   imagery: sources,
63120                   features: {}
63121                 };
63122
63123                 // use which-polygon to support efficient index and querying for imagery
63124                 const features = sources.map(source => {
63125                   if (!source.polygon) return null;
63126                   // workaround for editor-layer-index weirdness..
63127                   // Add an extra array nest to each element in `source.polygon`
63128                   // so the rings are not treated as a bunch of holes:
63129                   // what we have: [ [[outer],[hole],[hole]] ]
63130                   // what we want: [ [[outer]],[[outer]],[[outer]] ]
63131                   const rings = source.polygon.map(ring => [ring]);
63132
63133                   const feature = {
63134                     type: 'Feature',
63135                     properties: { id: source.id },
63136                     geometry: { type: 'MultiPolygon', coordinates: rings }
63137                   };
63138
63139                   _imageryIndex.features[source.id] = feature;
63140                   return feature;
63141
63142                 }).filter(Boolean);
63143
63144                 _imageryIndex.query = whichPolygon_1({ type: 'FeatureCollection', features: features });
63145
63146
63147                 // Instantiate `rendererBackgroundSource` objects for each source
63148                 _imageryIndex.backgrounds = sources.map(source => {
63149                   if (source.type === 'bing') {
63150                     return rendererBackgroundSource.Bing(source, dispatch$1);
63151                   } else if (/^EsriWorldImagery/.test(source.id)) {
63152                     return rendererBackgroundSource.Esri(source);
63153                   } else {
63154                     return rendererBackgroundSource(source);
63155                   }
63156                 });
63157
63158                 // Add 'None'
63159                 _imageryIndex.backgrounds.unshift(rendererBackgroundSource.None());
63160
63161                 // Add 'Custom'
63162                 let template = corePreferences('background-custom-template') || '';
63163                 const custom = rendererBackgroundSource.Custom(template);
63164                 _imageryIndex.backgrounds.unshift(custom);
63165
63166                 return _imageryIndex;
63167               });
63168           }
63169
63170
63171           function background(selection) {
63172             const currSource = baseLayer.source();
63173
63174             // If we are displaying an Esri basemap at high zoom,
63175             // check its tilemap to see how high the zoom can go
63176             if (context.map().zoom() > 18) {
63177               if (currSource && /^EsriWorldImagery/.test(currSource.id)) {
63178                 const center = context.map().center();
63179                 currSource.fetchTilemap(center);
63180               }
63181             }
63182
63183             // Is the imagery valid here? - #4827
63184             const sources = background.sources(context.map().extent());
63185             const wasValid = _isValid;
63186             _isValid = !!sources.filter(d => d === currSource).length;
63187
63188             if (wasValid !== _isValid) {      // change in valid status
63189               background.updateImagery();
63190             }
63191
63192
63193             let baseFilter = '';
63194             if (detected.cssfilters) {
63195               if (_brightness !== 1) {
63196                 baseFilter += ` brightness(${_brightness})`;
63197               }
63198               if (_contrast !== 1) {
63199                 baseFilter += ` contrast(${_contrast})`;
63200               }
63201               if (_saturation !== 1) {
63202                 baseFilter += ` saturate(${_saturation})`;
63203               }
63204               if (_sharpness < 1) {  // gaussian blur
63205                 const blur = d3_interpolateNumber(0.5, 5)(1 - _sharpness);
63206                 baseFilter += ` blur(${blur}px)`;
63207               }
63208             }
63209
63210             let base = selection.selectAll('.layer-background')
63211               .data([0]);
63212
63213             base = base.enter()
63214               .insert('div', '.layer-data')
63215               .attr('class', 'layer layer-background')
63216               .merge(base);
63217
63218             if (detected.cssfilters) {
63219               base.style('filter', baseFilter || null);
63220             } else {
63221               base.style('opacity', _brightness);
63222             }
63223
63224
63225             let imagery = base.selectAll('.layer-imagery')
63226               .data([0]);
63227
63228             imagery.enter()
63229               .append('div')
63230               .attr('class', 'layer layer-imagery')
63231               .merge(imagery)
63232               .call(baseLayer);
63233
63234
63235             let maskFilter = '';
63236             let mixBlendMode = '';
63237             if (detected.cssfilters && _sharpness > 1) {  // apply unsharp mask
63238               mixBlendMode = 'overlay';
63239               maskFilter = 'saturate(0) blur(3px) invert(1)';
63240
63241               let contrast = _sharpness - 1;
63242               maskFilter += ` contrast(${contrast})`;
63243
63244               let brightness = d3_interpolateNumber(1, 0.85)(_sharpness - 1);
63245               maskFilter += ` brightness(${brightness})`;
63246             }
63247
63248             let mask = base.selectAll('.layer-unsharp-mask')
63249               .data(detected.cssfilters && _sharpness > 1 ? [0] : []);
63250
63251             mask.exit()
63252               .remove();
63253
63254             mask.enter()
63255               .append('div')
63256               .attr('class', 'layer layer-mask layer-unsharp-mask')
63257               .merge(mask)
63258               .call(baseLayer)
63259               .style('filter', maskFilter || null)
63260               .style('mix-blend-mode', mixBlendMode || null);
63261
63262
63263             let overlays = selection.selectAll('.layer-overlay')
63264               .data(_overlayLayers, d => d.source().name());
63265
63266             overlays.exit()
63267               .remove();
63268
63269             overlays.enter()
63270               .insert('div', '.layer-data')
63271               .attr('class', 'layer layer-overlay')
63272               .merge(overlays)
63273               .each((layer, i, nodes) => select(nodes[i]).call(layer));
63274           }
63275
63276
63277           background.updateImagery = function() {
63278             let currSource = baseLayer.source();
63279             if (context.inIntro() || !currSource) return;
63280
63281             let o = _overlayLayers
63282               .filter(d => !d.source().isLocatorOverlay() && !d.source().isHidden())
63283               .map(d => d.source().id)
63284               .join(',');
63285
63286             const meters = geoOffsetToMeters(currSource.offset());
63287             const EPSILON = 0.01;
63288             const x = +meters[0].toFixed(2);
63289             const y = +meters[1].toFixed(2);
63290             let hash = utilStringQs(window.location.hash);
63291
63292             let id = currSource.id;
63293             if (id === 'custom') {
63294               id = `custom:${currSource.template()}`;
63295             }
63296
63297             if (id) {
63298               hash.background = id;
63299             } else {
63300               delete hash.background;
63301             }
63302
63303             if (o) {
63304               hash.overlays = o;
63305             } else {
63306               delete hash.overlays;
63307             }
63308
63309             if (Math.abs(x) > EPSILON || Math.abs(y) > EPSILON) {
63310               hash.offset = `${x},${y}`;
63311             } else {
63312               delete hash.offset;
63313             }
63314
63315             if (!window.mocha) {
63316               window.location.replace('#' + utilQsString(hash, true));
63317             }
63318
63319             let imageryUsed = [];
63320             let photoOverlaysUsed = [];
63321
63322             const currUsed = currSource.imageryUsed();
63323             if (currUsed && _isValid) {
63324               imageryUsed.push(currUsed);
63325             }
63326
63327             _overlayLayers
63328               .filter(d => !d.source().isLocatorOverlay() && !d.source().isHidden())
63329               .forEach(d => imageryUsed.push(d.source().imageryUsed()));
63330
63331             const dataLayer = context.layers().layer('data');
63332             if (dataLayer && dataLayer.enabled() && dataLayer.hasData()) {
63333               imageryUsed.push(dataLayer.getSrc());
63334             }
63335
63336             const photoOverlayLayers = {
63337               streetside: 'Bing Streetside',
63338               mapillary: 'Mapillary Images',
63339               'mapillary-map-features': 'Mapillary Map Features',
63340               'mapillary-signs': 'Mapillary Signs',
63341               openstreetcam: 'OpenStreetCam Images'
63342             };
63343
63344             for (let layerID in photoOverlayLayers) {
63345               const layer = context.layers().layer(layerID);
63346               if (layer && layer.enabled()) {
63347                 photoOverlaysUsed.push(layerID);
63348                 imageryUsed.push(photoOverlayLayers[layerID]);
63349               }
63350             }
63351
63352             context.history().imageryUsed(imageryUsed);
63353             context.history().photoOverlaysUsed(photoOverlaysUsed);
63354           };
63355
63356
63357           background.sources = (extent, zoom, includeCurrent) => {
63358             if (!_imageryIndex) return [];   // called before init()?
63359
63360             let visible = {};
63361             (_imageryIndex.query.bbox(extent.rectangle(), true) || [])
63362               .forEach(d => visible[d.id] = true);
63363
63364             const currSource = baseLayer.source();
63365
63366             return _imageryIndex.backgrounds.filter(source => {
63367               if (!source.polygon) return true;                          // always include imagery with worldwide coverage
63368               if (includeCurrent && currSource === source) return true;  // optionally include the current imagery
63369               if (zoom && zoom < 6) return false;                        // optionally exclude local imagery at low zooms
63370               return visible[source.id];                                 // include imagery visible in given extent
63371             });
63372           };
63373
63374
63375           background.dimensions = (val) => {
63376             if (!val) return;
63377             baseLayer.dimensions(val);
63378             _overlayLayers.forEach(layer => layer.dimensions(val));
63379           };
63380
63381
63382           background.baseLayerSource = function(d) {
63383             if (!arguments.length) return baseLayer.source();
63384
63385             // test source against OSM imagery blacklists..
63386             const osm = context.connection();
63387             if (!osm) return background;
63388
63389             const blacklists = osm.imageryBlacklists();
63390             const template = d.template();
63391             let fail = false;
63392             let tested = 0;
63393             let regex;
63394
63395             for (let i = 0; i < blacklists.length; i++) {
63396               try {
63397                 regex = new RegExp(blacklists[i]);
63398                 fail = regex.test(template);
63399                 tested++;
63400                 if (fail) break;
63401               } catch (e) {
63402                 /* noop */
63403               }
63404             }
63405
63406             // ensure at least one test was run.
63407             if (!tested) {
63408               regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*');
63409               fail = regex.test(template);
63410             }
63411
63412             baseLayer.source(!fail ? d : background.findSource('none'));
63413             dispatch$1.call('change');
63414             background.updateImagery();
63415             return background;
63416           };
63417
63418
63419           background.findSource = (id) => {
63420             if (!id || !_imageryIndex) return null;   // called before init()?
63421             return _imageryIndex.backgrounds.find(d => d.id && d.id === id);
63422           };
63423
63424
63425           background.bing = () => {
63426             background.baseLayerSource(background.findSource('Bing'));
63427           };
63428
63429
63430           background.showsLayer = (d) => {
63431             const currSource = baseLayer.source();
63432             if (!d || !currSource) return false;
63433             return d.id === currSource.id || _overlayLayers.some(layer => d.id === layer.source().id);
63434           };
63435
63436
63437           background.overlayLayerSources = () => {
63438             return _overlayLayers.map(layer => layer.source());
63439           };
63440
63441
63442           background.toggleOverlayLayer = (d) => {
63443             let layer;
63444             for (let i = 0; i < _overlayLayers.length; i++) {
63445               layer = _overlayLayers[i];
63446               if (layer.source() === d) {
63447                 _overlayLayers.splice(i, 1);
63448                 dispatch$1.call('change');
63449                 background.updateImagery();
63450                 return;
63451               }
63452             }
63453
63454             layer = rendererTileLayer(context)
63455               .source(d)
63456               .projection(context.projection)
63457               .dimensions(baseLayer.dimensions()
63458             );
63459
63460             _overlayLayers.push(layer);
63461             dispatch$1.call('change');
63462             background.updateImagery();
63463           };
63464
63465
63466           background.nudge = (d, zoom) => {
63467             const currSource = baseLayer.source();
63468             if (currSource) {
63469               currSource.nudge(d, zoom);
63470               dispatch$1.call('change');
63471               background.updateImagery();
63472             }
63473             return background;
63474           };
63475
63476
63477           background.offset = function(d) {
63478             const currSource = baseLayer.source();
63479             if (!arguments.length) {
63480               return (currSource && currSource.offset()) || [0, 0];
63481             }
63482             if (currSource) {
63483               currSource.offset(d);
63484               dispatch$1.call('change');
63485               background.updateImagery();
63486             }
63487             return background;
63488           };
63489
63490
63491           background.brightness = function(d) {
63492             if (!arguments.length) return _brightness;
63493             _brightness = d;
63494             if (context.mode()) dispatch$1.call('change');
63495             return background;
63496           };
63497
63498
63499           background.contrast = function(d) {
63500             if (!arguments.length) return _contrast;
63501             _contrast = d;
63502             if (context.mode()) dispatch$1.call('change');
63503             return background;
63504           };
63505
63506
63507           background.saturation = function(d) {
63508             if (!arguments.length) return _saturation;
63509             _saturation = d;
63510             if (context.mode()) dispatch$1.call('change');
63511             return background;
63512           };
63513
63514
63515           background.sharpness = function(d) {
63516             if (!arguments.length) return _sharpness;
63517             _sharpness = d;
63518             if (context.mode()) dispatch$1.call('change');
63519             return background;
63520           };
63521
63522           let _loadPromise;
63523
63524           background.ensureLoaded = () => {
63525
63526             if (_loadPromise) return _loadPromise;
63527
63528             function parseMapParams(qmap) {
63529               if (!qmap) return false;
63530               const params = qmap.split('/').map(Number);
63531               if (params.length < 3 || params.some(isNaN)) return false;
63532               return geoExtent([params[2], params[1]]);  // lon,lat
63533             }
63534
63535             const hash = utilStringQs(window.location.hash);
63536             const requested = hash.background || hash.layer;
63537             let extent = parseMapParams(hash.map);
63538
63539             return _loadPromise = ensureImageryIndex()
63540               .then(imageryIndex => {
63541                 const first = imageryIndex.backgrounds.length && imageryIndex.backgrounds[0];
63542
63543                 let best;
63544                 if (!requested && extent) {
63545                   best = background.sources(extent).find(s => s.best());
63546                 }
63547
63548                 // Decide which background layer to display
63549                 if (requested && requested.indexOf('custom:') === 0) {
63550                   const template = requested.replace(/^custom:/, '');
63551                   const custom = background.findSource('custom');
63552                   background.baseLayerSource(custom.template(template));
63553                   corePreferences('background-custom-template', template);
63554                 } else {
63555                   background.baseLayerSource(
63556                     background.findSource(requested) ||
63557                     best ||
63558                     background.findSource(corePreferences('background-last-used')) ||
63559                     background.findSource('Bing') ||
63560                     first ||
63561                     background.findSource('none')
63562                   );
63563                 }
63564
63565                 const locator = imageryIndex.backgrounds.find(d => d.overlay && d.default);
63566                 if (locator) {
63567                   background.toggleOverlayLayer(locator);
63568                 }
63569
63570                 const overlays = (hash.overlays || '').split(',');
63571                 overlays.forEach(overlay => {
63572                   overlay = background.findSource(overlay);
63573                   if (overlay) {
63574                     background.toggleOverlayLayer(overlay);
63575                   }
63576                 });
63577
63578                 if (hash.gpx) {
63579                   const gpx = context.layers().layer('data');
63580                   if (gpx) {
63581                     gpx.url(hash.gpx, '.gpx');
63582                   }
63583                 }
63584
63585                 if (hash.offset) {
63586                   const offset = hash.offset
63587                     .replace(/;/g, ',')
63588                     .split(',')
63589                     .map(n => !isNaN(n) && n);
63590
63591                   if (offset.length === 2) {
63592                     background.offset(geoMetersToOffset(offset));
63593                   }
63594                 }
63595               })
63596               .catch(() => { /* ignore */ });
63597           };
63598
63599
63600           return utilRebind(background, dispatch$1, 'on');
63601         }
63602
63603         function rendererFeatures(context) {
63604             var dispatch$1 = dispatch('change', 'redraw');
63605             var features = utilRebind({}, dispatch$1, 'on');
63606             var _deferred = new Set();
63607
63608             var traffic_roads = {
63609                 'motorway': true,
63610                 'motorway_link': true,
63611                 'trunk': true,
63612                 'trunk_link': true,
63613                 'primary': true,
63614                 'primary_link': true,
63615                 'secondary': true,
63616                 'secondary_link': true,
63617                 'tertiary': true,
63618                 'tertiary_link': true,
63619                 'residential': true,
63620                 'unclassified': true,
63621                 'living_street': true
63622             };
63623
63624             var service_roads = {
63625                 'service': true,
63626                 'road': true,
63627                 'track': true
63628             };
63629
63630             var paths = {
63631                 'path': true,
63632                 'footway': true,
63633                 'cycleway': true,
63634                 'bridleway': true,
63635                 'steps': true,
63636                 'pedestrian': true
63637             };
63638
63639             var past_futures = {
63640                 'proposed': true,
63641                 'construction': true,
63642                 'abandoned': true,
63643                 'dismantled': true,
63644                 'disused': true,
63645                 'razed': true,
63646                 'demolished': true,
63647                 'obliterated': true
63648             };
63649
63650             var _cullFactor = 1;
63651             var _cache = {};
63652             var _rules = {};
63653             var _stats = {};
63654             var _keys = [];
63655             var _hidden = [];
63656             var _forceVisible = {};
63657
63658
63659             function update() {
63660                 if (!window.mocha) {
63661                     var hash = utilStringQs(window.location.hash);
63662                     var disabled = features.disabled();
63663                     if (disabled.length) {
63664                         hash.disable_features = disabled.join(',');
63665                     } else {
63666                         delete hash.disable_features;
63667                     }
63668                     window.location.replace('#' + utilQsString(hash, true));
63669                     corePreferences('disabled-features', disabled.join(','));
63670                 }
63671                 _hidden = features.hidden();
63672                 dispatch$1.call('change');
63673                 dispatch$1.call('redraw');
63674             }
63675
63676
63677             function defineRule(k, filter, max) {
63678                 var isEnabled = true;
63679
63680                 _keys.push(k);
63681                 _rules[k] = {
63682                     filter: filter,
63683                     enabled: isEnabled,   // whether the user wants it enabled..
63684                     count: 0,
63685                     currentMax: (max || Infinity),
63686                     defaultMax: (max || Infinity),
63687                     enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
63688                     disable: function() { this.enabled = false; this.currentMax = 0; },
63689                     hidden: function() {
63690                         return (this.count === 0 && !this.enabled) ||
63691                             this.count > this.currentMax * _cullFactor;
63692                     },
63693                     autoHidden: function() { return this.hidden() && this.currentMax > 0; }
63694                 };
63695             }
63696
63697
63698             defineRule('points', function isPoint(tags, geometry) {
63699                 return geometry === 'point';
63700             }, 200);
63701
63702             defineRule('traffic_roads', function isTrafficRoad(tags) {
63703                 return traffic_roads[tags.highway];
63704             });
63705
63706             defineRule('service_roads', function isServiceRoad(tags) {
63707                 return service_roads[tags.highway];
63708             });
63709
63710             defineRule('paths', function isPath(tags) {
63711                 return paths[tags.highway];
63712             });
63713
63714             defineRule('buildings', function isBuilding(tags) {
63715                 return (
63716                     (!!tags.building && tags.building !== 'no') ||
63717                     tags.parking === 'multi-storey' ||
63718                     tags.parking === 'sheds' ||
63719                     tags.parking === 'carports' ||
63720                     tags.parking === 'garage_boxes'
63721                 );
63722             }, 250);
63723
63724             defineRule('building_parts', function isBuildingPart(tags) {
63725                 return tags['building:part'];
63726             });
63727
63728             defineRule('indoor', function isIndoor(tags) {
63729                 return tags.indoor;
63730             });
63731
63732             defineRule('landuse', function isLanduse(tags, geometry) {
63733                 return geometry === 'area' &&
63734                     !_rules.buildings.filter(tags) &&
63735                     !_rules.building_parts.filter(tags) &&
63736                     !_rules.indoor.filter(tags) &&
63737                     !_rules.water.filter(tags) &&
63738                     !_rules.pistes.filter(tags);
63739             });
63740
63741             defineRule('boundaries', function isBoundary(tags) {
63742                 return (
63743                     !!tags.boundary
63744                 ) && !(
63745                     traffic_roads[tags.highway] ||
63746                     service_roads[tags.highway] ||
63747                     paths[tags.highway] ||
63748                     tags.waterway ||
63749                     tags.railway ||
63750                     tags.landuse ||
63751                     tags.natural ||
63752                     tags.building ||
63753                     tags.power
63754                 );
63755             });
63756
63757             defineRule('water', function isWater(tags) {
63758                 return (
63759                     !!tags.waterway ||
63760                     tags.natural === 'water' ||
63761                     tags.natural === 'coastline' ||
63762                     tags.natural === 'bay' ||
63763                     tags.landuse === 'pond' ||
63764                     tags.landuse === 'basin' ||
63765                     tags.landuse === 'reservoir' ||
63766                     tags.landuse === 'salt_pond'
63767                 );
63768             });
63769
63770             defineRule('rail', function isRail(tags) {
63771                 return (
63772                     !!tags.railway ||
63773                     tags.landuse === 'railway'
63774                 ) && !(
63775                     traffic_roads[tags.highway] ||
63776                     service_roads[tags.highway] ||
63777                     paths[tags.highway]
63778                 );
63779             });
63780
63781             defineRule('pistes', function isPiste(tags) {
63782                 return tags['piste:type'];
63783             });
63784
63785             defineRule('aerialways', function isPiste(tags) {
63786                 return tags.aerialway &&
63787                     tags.aerialway !== 'yes' &&
63788                     tags.aerialway !== 'station';
63789             });
63790
63791             defineRule('power', function isPower(tags) {
63792                 return !!tags.power;
63793             });
63794
63795             // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
63796             defineRule('past_future', function isPastFuture(tags) {
63797                 if (
63798                     traffic_roads[tags.highway] ||
63799                     service_roads[tags.highway] ||
63800                     paths[tags.highway]
63801                 ) { return false; }
63802
63803                 var strings = Object.keys(tags);
63804
63805                 for (var i = 0; i < strings.length; i++) {
63806                     var s = strings[i];
63807                     if (past_futures[s] || past_futures[tags[s]]) { return true; }
63808                 }
63809                 return false;
63810             });
63811
63812             // Lines or areas that don't match another feature filter.
63813             // IMPORTANT: The 'others' feature must be the last one defined,
63814             //   so that code in getMatches can skip this test if `hasMatch = true`
63815             defineRule('others', function isOther(tags, geometry) {
63816                 return (geometry === 'line' || geometry === 'area');
63817             });
63818
63819
63820
63821             features.features = function() {
63822                 return _rules;
63823             };
63824
63825
63826             features.keys = function() {
63827                 return _keys;
63828             };
63829
63830
63831             features.enabled = 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.disabled = function(k) {
63840                 if (!arguments.length) {
63841                     return _keys.filter(function(k) { return !_rules[k].enabled; });
63842                 }
63843                 return _rules[k] && !_rules[k].enabled;
63844             };
63845
63846
63847             features.hidden = function(k) {
63848                 if (!arguments.length) {
63849                     return _keys.filter(function(k) { return _rules[k].hidden(); });
63850                 }
63851                 return _rules[k] && _rules[k].hidden();
63852             };
63853
63854
63855             features.autoHidden = function(k) {
63856                 if (!arguments.length) {
63857                     return _keys.filter(function(k) { return _rules[k].autoHidden(); });
63858                 }
63859                 return _rules[k] && _rules[k].autoHidden();
63860             };
63861
63862
63863             features.enable = function(k) {
63864                 if (_rules[k] && !_rules[k].enabled) {
63865                     _rules[k].enable();
63866                     update();
63867                 }
63868             };
63869
63870             features.enableAll = function() {
63871                 var didEnable = false;
63872                 for (var k in _rules) {
63873                     if (!_rules[k].enabled) {
63874                         didEnable = true;
63875                         _rules[k].enable();
63876                     }
63877                 }
63878                 if (didEnable) update();
63879             };
63880
63881
63882             features.disable = function(k) {
63883                 if (_rules[k] && _rules[k].enabled) {
63884                     _rules[k].disable();
63885                     update();
63886                 }
63887             };
63888
63889             features.disableAll = function() {
63890                 var didDisable = false;
63891                 for (var k in _rules) {
63892                     if (_rules[k].enabled) {
63893                         didDisable = true;
63894                         _rules[k].disable();
63895                     }
63896                 }
63897                 if (didDisable) update();
63898             };
63899
63900
63901             features.toggle = function(k) {
63902                 if (_rules[k]) {
63903                     (function(f) { return f.enabled ? f.disable() : f.enable(); }(_rules[k]));
63904                     update();
63905                 }
63906             };
63907
63908
63909             features.resetStats = function() {
63910                 for (var i = 0; i < _keys.length; i++) {
63911                     _rules[_keys[i]].count = 0;
63912                 }
63913                 dispatch$1.call('change');
63914             };
63915
63916
63917             features.gatherStats = function(d, resolver, dimensions) {
63918                 var needsRedraw = false;
63919                 var types = utilArrayGroupBy(d, 'type');
63920                 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
63921                 var currHidden, geometry, matches, i, j;
63922
63923                 for (i = 0; i < _keys.length; i++) {
63924                     _rules[_keys[i]].count = 0;
63925                 }
63926
63927                 // adjust the threshold for point/building culling based on viewport size..
63928                 // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
63929                 _cullFactor = dimensions[0] * dimensions[1] / 1000000;
63930
63931                 for (i = 0; i < entities.length; i++) {
63932                     geometry = entities[i].geometry(resolver);
63933                     matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
63934                     for (j = 0; j < matches.length; j++) {
63935                         _rules[matches[j]].count++;
63936                     }
63937                 }
63938
63939                 currHidden = features.hidden();
63940                 if (currHidden !== _hidden) {
63941                     _hidden = currHidden;
63942                     needsRedraw = true;
63943                     dispatch$1.call('change');
63944                 }
63945
63946                 return needsRedraw;
63947             };
63948
63949
63950             features.stats = function() {
63951                 for (var i = 0; i < _keys.length; i++) {
63952                     _stats[_keys[i]] = _rules[_keys[i]].count;
63953                 }
63954
63955                 return _stats;
63956             };
63957
63958
63959             features.clear = function(d) {
63960                 for (var i = 0; i < d.length; i++) {
63961                     features.clearEntity(d[i]);
63962                 }
63963             };
63964
63965
63966             features.clearEntity = function(entity) {
63967                 delete _cache[osmEntity.key(entity)];
63968             };
63969
63970
63971             features.reset = function() {
63972                 Array.from(_deferred).forEach(function(handle) {
63973                     window.cancelIdleCallback(handle);
63974                     _deferred.delete(handle);
63975                 });
63976
63977                 _cache = {};
63978             };
63979
63980             // only certain relations are worth checking
63981             function relationShouldBeChecked(relation) {
63982                 // multipolygon features have `area` geometry and aren't checked here
63983                 return relation.tags.type === 'boundary';
63984             }
63985
63986             features.getMatches = function(entity, resolver, geometry) {
63987                 if (geometry === 'vertex' ||
63988                     (geometry === 'relation' && !relationShouldBeChecked(entity))) return {};
63989
63990                 var ent = osmEntity.key(entity);
63991                 if (!_cache[ent]) {
63992                     _cache[ent] = {};
63993                 }
63994
63995                 if (!_cache[ent].matches) {
63996                     var matches = {};
63997                     var hasMatch = false;
63998
63999                     for (var i = 0; i < _keys.length; i++) {
64000                         if (_keys[i] === 'others') {
64001                             if (hasMatch) continue;
64002
64003                             // If an entity...
64004                             //   1. is a way that hasn't matched other 'interesting' feature rules,
64005                             if (entity.type === 'way') {
64006                                 var parents = features.getParents(entity, resolver, geometry);
64007
64008                                 //   2a. belongs only to a single multipolygon relation
64009                                 if ((parents.length === 1 && parents[0].isMultipolygon()) ||
64010                                     // 2b. or belongs only to boundary relations
64011                                     (parents.length > 0 && parents.every(function(parent) { return parent.tags.type === 'boundary'; }))) {
64012
64013                                     // ...then match whatever feature rules the parent relation has matched.
64014                                     // see #2548, #2887
64015                                     //
64016                                     // IMPORTANT:
64017                                     // For this to work, getMatches must be called on relations before ways.
64018                                     //
64019                                     var pkey = osmEntity.key(parents[0]);
64020                                     if (_cache[pkey] && _cache[pkey].matches) {
64021                                         matches = Object.assign({}, _cache[pkey].matches);  // shallow copy
64022                                         continue;
64023                                     }
64024                                 }
64025                             }
64026                         }
64027
64028                         if (_rules[_keys[i]].filter(entity.tags, geometry)) {
64029                             matches[_keys[i]] = hasMatch = true;
64030                         }
64031                     }
64032                     _cache[ent].matches = matches;
64033                 }
64034
64035                 return _cache[ent].matches;
64036             };
64037
64038
64039             features.getParents = function(entity, resolver, geometry) {
64040                 if (geometry === 'point') return [];
64041
64042                 var ent = osmEntity.key(entity);
64043                 if (!_cache[ent]) {
64044                     _cache[ent] = {};
64045                 }
64046
64047                 if (!_cache[ent].parents) {
64048                     var parents = [];
64049                     if (geometry === 'vertex') {
64050                         parents = resolver.parentWays(entity);
64051                     } else {   // 'line', 'area', 'relation'
64052                         parents = resolver.parentRelations(entity);
64053                     }
64054                     _cache[ent].parents = parents;
64055                 }
64056                 return _cache[ent].parents;
64057             };
64058
64059
64060             features.isHiddenPreset = function(preset, geometry) {
64061                 if (!_hidden.length) return false;
64062                 if (!preset.tags) return false;
64063
64064                 var test = preset.setTags({}, geometry);
64065                 for (var key in _rules) {
64066                     if (_rules[key].filter(test, geometry)) {
64067                         if (_hidden.indexOf(key) !== -1) {
64068                             return key;
64069                         }
64070                         return false;
64071                     }
64072                 }
64073                 return false;
64074             };
64075
64076
64077             features.isHiddenFeature = function(entity, resolver, geometry) {
64078                 if (!_hidden.length) return false;
64079                 if (!entity.version) return false;
64080                 if (_forceVisible[entity.id]) return false;
64081
64082                 var matches = Object.keys(features.getMatches(entity, resolver, geometry));
64083                 return matches.length && matches.every(function(k) { return features.hidden(k); });
64084             };
64085
64086
64087             features.isHiddenChild = function(entity, resolver, geometry) {
64088                 if (!_hidden.length) return false;
64089                 if (!entity.version || geometry === 'point') return false;
64090                 if (_forceVisible[entity.id]) return false;
64091
64092                 var parents = features.getParents(entity, resolver, geometry);
64093                 if (!parents.length) return false;
64094
64095                 for (var i = 0; i < parents.length; i++) {
64096                     if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
64097                         return false;
64098                     }
64099                 }
64100                 return true;
64101             };
64102
64103
64104             features.hasHiddenConnections = function(entity, resolver) {
64105                 if (!_hidden.length) return false;
64106
64107                 var childNodes, connections;
64108                 if (entity.type === 'midpoint') {
64109                     childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
64110                     connections = [];
64111                 } else {
64112                     childNodes = entity.nodes ? resolver.childNodes(entity) : [];
64113                     connections = features.getParents(entity, resolver, entity.geometry(resolver));
64114                 }
64115
64116                 // gather ways connected to child nodes..
64117                 connections = childNodes.reduce(function(result, e) {
64118                     return resolver.isShared(e) ? utilArrayUnion(result, resolver.parentWays(e)) : result;
64119                 }, connections);
64120
64121                 return connections.some(function(e) {
64122                     return features.isHidden(e, resolver, e.geometry(resolver));
64123                 });
64124             };
64125
64126
64127             features.isHidden = function(entity, resolver, geometry) {
64128                 if (!_hidden.length) return false;
64129                 if (!entity.version) return false;
64130
64131                 var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
64132                 return fn(entity, resolver, geometry);
64133             };
64134
64135
64136             features.filter = function(d, resolver) {
64137                 if (!_hidden.length) return d;
64138
64139                 var result = [];
64140                 for (var i = 0; i < d.length; i++) {
64141                     var entity = d[i];
64142                     if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
64143                         result.push(entity);
64144                     }
64145                 }
64146                 return result;
64147             };
64148
64149
64150             features.forceVisible = function(entityIDs) {
64151                 if (!arguments.length) return Object.keys(_forceVisible);
64152
64153                 _forceVisible = {};
64154                 for (var i = 0; i < entityIDs.length; i++) {
64155                     _forceVisible[entityIDs[i]] = true;
64156                     var entity = context.hasEntity(entityIDs[i]);
64157                     if (entity && entity.type === 'relation') {
64158                         // also show relation members (one level deep)
64159                         for (var j in entity.members) {
64160                             _forceVisible[entity.members[j].id] = true;
64161                         }
64162                     }
64163                 }
64164                 return features;
64165             };
64166
64167
64168             features.init = function() {
64169                 var storage = corePreferences('disabled-features');
64170                 if (storage) {
64171                     var storageDisabled = storage.replace(/;/g, ',').split(',');
64172                     storageDisabled.forEach(features.disable);
64173                 }
64174
64175                 var hash = utilStringQs(window.location.hash);
64176                 if (hash.disable_features) {
64177                     var hashDisabled = hash.disable_features.replace(/;/g, ',').split(',');
64178                     hashDisabled.forEach(features.disable);
64179                 }
64180             };
64181
64182
64183             // warm up the feature matching cache upon merging fetched data
64184             context.history().on('merge.features', function(newEntities) {
64185                 if (!newEntities) return;
64186                 var handle = window.requestIdleCallback(function() {
64187                     var graph = context.graph();
64188                     var types = utilArrayGroupBy(newEntities, 'type');
64189                     // ensure that getMatches is called on relations before ways
64190                     var entities = [].concat(types.relation || [], types.way || [], types.node || []);
64191                     for (var i = 0; i < entities.length; i++) {
64192                         var geometry = entities[i].geometry(graph);
64193                         features.getMatches(entities[i], graph, geometry);
64194                     }
64195                 });
64196                 _deferred.add(handle);
64197             });
64198
64199
64200             return features;
64201         }
64202
64203         // Touch targets control which other vertices we can drag a vertex onto.
64204         //
64205         // - the activeID - nope
64206         // - 1 away (adjacent) to the activeID - yes (vertices will be merged)
64207         // - 2 away from the activeID - nope (would create a self intersecting segment)
64208         // - all others on a linear way - yes
64209         // - all others on a closed way - nope (would create a self intersecting polygon)
64210         //
64211         // returns
64212         // 0 = active vertex - no touch/connect
64213         // 1 = passive vertex - yes touch/connect
64214         // 2 = adjacent vertex - yes but pay attention segmenting a line here
64215         //
64216         function svgPassiveVertex(node, graph, activeID) {
64217             if (!activeID) return 1;
64218             if (activeID === node.id) return 0;
64219
64220             var parents = graph.parentWays(node);
64221
64222             var i, j, nodes, isClosed, ix1, ix2, ix3, ix4, max;
64223
64224             for (i = 0; i < parents.length; i++) {
64225                 nodes = parents[i].nodes;
64226                 isClosed = parents[i].isClosed();
64227                 for (j = 0; j < nodes.length; j++) {   // find this vertex, look nearby
64228                     if (nodes[j] === node.id) {
64229                         ix1 = j - 2;
64230                         ix2 = j - 1;
64231                         ix3 = j + 1;
64232                         ix4 = j + 2;
64233
64234                         if (isClosed) {  // wraparound if needed
64235                             max = nodes.length - 1;
64236                             if (ix1 < 0)   ix1 = max + ix1;
64237                             if (ix2 < 0)   ix2 = max + ix2;
64238                             if (ix3 > max) ix3 = ix3 - max;
64239                             if (ix4 > max) ix4 = ix4 - max;
64240                         }
64241
64242                         if (nodes[ix1] === activeID) return 0;        // no - prevent self intersect
64243                         else if (nodes[ix2] === activeID) return 2;   // ok - adjacent
64244                         else if (nodes[ix3] === activeID) return 2;   // ok - adjacent
64245                         else if (nodes[ix4] === activeID) return 0;   // no - prevent self intersect
64246                         else if (isClosed && nodes.indexOf(activeID) !== -1) return 0;  // no - prevent self intersect
64247                     }
64248                 }
64249             }
64250
64251             return 1;   // ok
64252         }
64253
64254
64255         function svgMarkerSegments(projection, graph, dt,
64256                                           shouldReverse,
64257                                           bothDirections) {
64258             return function(entity) {
64259                 var i = 0;
64260                 var offset = dt;
64261                 var segments = [];
64262                 var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
64263                 var coordinates = graph.childNodes(entity).map(function(n) { return n.loc; });
64264                 var a, b;
64265
64266                 if (shouldReverse(entity)) {
64267                     coordinates.reverse();
64268                 }
64269
64270                 d3_geoStream({
64271                     type: 'LineString',
64272                     coordinates: coordinates
64273                 }, projection.stream(clip({
64274                     lineStart: function() {},
64275                     lineEnd: function() { a = null; },
64276                     point: function(x, y) {
64277                         b = [x, y];
64278
64279                         if (a) {
64280                             var span = geoVecLength(a, b) - offset;
64281
64282                             if (span >= 0) {
64283                                 var heading = geoVecAngle(a, b);
64284                                 var dx = dt * Math.cos(heading);
64285                                 var dy = dt * Math.sin(heading);
64286                                 var p = [
64287                                     a[0] + offset * Math.cos(heading),
64288                                     a[1] + offset * Math.sin(heading)
64289                                 ];
64290
64291                                 // gather coordinates
64292                                 var coord = [a, p];
64293                                 for (span -= dt; span >= 0; span -= dt) {
64294                                     p = geoVecAdd(p, [dx, dy]);
64295                                     coord.push(p);
64296                                 }
64297                                 coord.push(b);
64298
64299                                 // generate svg paths
64300                                 var segment = '';
64301                                 var j;
64302
64303                                 for (j = 0; j < coord.length; j++) {
64304                                     segment += (j === 0 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
64305                                 }
64306                                 segments.push({ id: entity.id, index: i++, d: segment });
64307
64308                                 if (bothDirections(entity)) {
64309                                     segment = '';
64310                                     for (j = coord.length - 1; j >= 0; j--) {
64311                                         segment += (j === coord.length - 1 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
64312                                     }
64313                                     segments.push({ id: entity.id, index: i++, d: segment });
64314                                 }
64315                             }
64316
64317                             offset = -span;
64318                         }
64319
64320                         a = b;
64321                     }
64322                 })));
64323
64324                 return segments;
64325             };
64326         }
64327
64328
64329         function svgPath(projection, graph, isArea) {
64330
64331             // Explanation of magic numbers:
64332             // "padding" here allows space for strokes to extend beyond the viewport,
64333             // so that the stroke isn't drawn along the edge of the viewport when
64334             // the shape is clipped.
64335             //
64336             // When drawing lines, pad viewport by 5px.
64337             // When drawing areas, pad viewport by 65px in each direction to allow
64338             // for 60px area fill stroke (see ".fill-partial path.fill" css rule)
64339
64340             var cache = {};
64341             var padding = isArea ? 65 : 5;
64342             var viewport = projection.clipExtent();
64343             var paddedExtent = [
64344                 [viewport[0][0] - padding, viewport[0][1] - padding],
64345                 [viewport[1][0] + padding, viewport[1][1] + padding]
64346             ];
64347             var clip = d3_geoIdentity().clipExtent(paddedExtent).stream;
64348             var project = projection.stream;
64349             var path = d3_geoPath()
64350                 .projection({stream: function(output) { return project(clip(output)); }});
64351
64352             var svgpath = function(entity) {
64353                 if (entity.id in cache) {
64354                     return cache[entity.id];
64355                 } else {
64356                     return cache[entity.id] = path(entity.asGeoJSON(graph));
64357                 }
64358             };
64359
64360             svgpath.geojson = function(d) {
64361                 if (d.__featurehash__ !== undefined) {
64362                     if (d.__featurehash__ in cache) {
64363                         return cache[d.__featurehash__];
64364                     } else {
64365                         return cache[d.__featurehash__] = path(d);
64366                     }
64367                 } else {
64368                     return path(d);
64369                 }
64370             };
64371
64372             return svgpath;
64373         }
64374
64375
64376         function svgPointTransform(projection) {
64377             var svgpoint = function(entity) {
64378                 // http://jsperf.com/short-array-join
64379                 var pt = projection(entity.loc);
64380                 return 'translate(' + pt[0] + ',' + pt[1] + ')';
64381             };
64382
64383             svgpoint.geojson = function(d) {
64384                 return svgpoint(d.properties.entity);
64385             };
64386
64387             return svgpoint;
64388         }
64389
64390
64391         function svgRelationMemberTags(graph) {
64392             return function(entity) {
64393                 var tags = entity.tags;
64394                 var shouldCopyMultipolygonTags = !entity.hasInterestingTags();
64395                 graph.parentRelations(entity).forEach(function(relation) {
64396                     var type = relation.tags.type;
64397                     if ((type === 'multipolygon' && shouldCopyMultipolygonTags) || type === 'boundary') {
64398                         tags = Object.assign({}, relation.tags, tags);
64399                     }
64400                 });
64401                 return tags;
64402             };
64403         }
64404
64405
64406         function svgSegmentWay(way, graph, activeID) {
64407             // When there is no activeID, we can memoize this expensive computation
64408             if (activeID === undefined) {
64409                 return graph.transient(way, 'waySegments', getWaySegments);
64410             } else {
64411                 return getWaySegments();
64412             }
64413
64414             function getWaySegments() {
64415                 var isActiveWay = (way.nodes.indexOf(activeID) !== -1);
64416                 var features = { passive: [], active: [] };
64417                 var start = {};
64418                 var end = {};
64419                 var node, type;
64420
64421                 for (var i = 0; i < way.nodes.length; i++) {
64422                     node = graph.entity(way.nodes[i]);
64423                     type = svgPassiveVertex(node, graph, activeID);
64424                     end = { node: node, type: type };
64425
64426                     if (start.type !== undefined) {
64427                         if (start.node.id === activeID || end.node.id === activeID) ; else if (isActiveWay && (start.type === 2 || end.type === 2)) {   // one adjacent vertex
64428                             pushActive(start, end, i);
64429                         } else if (start.type === 0 && end.type === 0) {   // both active vertices
64430                             pushActive(start, end, i);
64431                         } else {
64432                             pushPassive(start, end, i);
64433                         }
64434                     }
64435
64436                     start = end;
64437                 }
64438
64439                 return features;
64440
64441                 function pushActive(start, end, index) {
64442                     features.active.push({
64443                         type: 'Feature',
64444                         id: way.id + '-' + index + '-nope',
64445                         properties: {
64446                             nope: true,
64447                             target: true,
64448                             entity: way,
64449                             nodes: [start.node, end.node],
64450                             index: index
64451                         },
64452                         geometry: {
64453                             type: 'LineString',
64454                             coordinates: [start.node.loc, end.node.loc]
64455                         }
64456                     });
64457                 }
64458
64459                 function pushPassive(start, end, index) {
64460                     features.passive.push({
64461                         type: 'Feature',
64462                         id: way.id + '-' + index,
64463                         properties: {
64464                             target: true,
64465                             entity: way,
64466                             nodes: [start.node, end.node],
64467                             index: index
64468                         },
64469                         geometry: {
64470                             type: 'LineString',
64471                             coordinates: [start.node.loc, end.node.loc]
64472                         }
64473                     });
64474                 }
64475             }
64476         }
64477
64478         function svgTagClasses() {
64479             var primaries = [
64480                 'building', 'highway', 'railway', 'waterway', 'aeroway', 'aerialway',
64481                 'piste:type', 'boundary', 'power', 'amenity', 'natural', 'landuse',
64482                 'leisure', 'military', 'place', 'man_made', 'route', 'attraction',
64483                 'building:part', 'indoor'
64484             ];
64485             var statuses = [
64486                 // nonexistent, might be built
64487                 'proposed', 'planned',
64488                 // under maintentance or between groundbreaking and opening
64489                 'construction',
64490                 // existent but not functional
64491                 'disused',
64492                 // dilapidated to nonexistent
64493                 'abandoned',
64494                 // nonexistent, still may appear in imagery
64495                 'dismantled', 'razed', 'demolished', 'obliterated',
64496                 // existent occasionally, e.g. stormwater drainage basin
64497                 'intermittent'
64498             ];
64499             var secondaries = [
64500                 'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
64501                 'surface', 'tracktype', 'footway', 'crossing', 'service', 'sport',
64502                 'public_transport', 'location', 'parking', 'golf', 'type', 'leisure',
64503                 'man_made', 'indoor'
64504             ];
64505             var _tags = function(entity) { return entity.tags; };
64506
64507
64508             var tagClasses = function(selection) {
64509                 selection.each(function tagClassesEach(entity) {
64510                     var value = this.className;
64511
64512                     if (value.baseVal !== undefined) {
64513                         value = value.baseVal;
64514                     }
64515
64516                     var t = _tags(entity);
64517
64518                     var computed = tagClasses.getClassesString(t, value);
64519
64520                     if (computed !== value) {
64521                         select(this).attr('class', computed);
64522                     }
64523                 });
64524             };
64525
64526
64527             tagClasses.getClassesString = function(t, value) {
64528                 var primary, status;
64529                 var i, j, k, v;
64530
64531                 // in some situations we want to render perimeter strokes a certain way
64532                 var overrideGeometry;
64533                 if (/\bstroke\b/.test(value)) {
64534                     if (!!t.barrier && t.barrier !== 'no') {
64535                         overrideGeometry = 'line';
64536                     }
64537                 }
64538
64539                 // preserve base classes (nothing with `tag-`)
64540                 var classes = value.trim().split(/\s+/)
64541                     .filter(function(klass) {
64542                         return klass.length && !/^tag-/.test(klass);
64543                     })
64544                     .map(function(klass) {  // special overrides for some perimeter strokes
64545                         return (klass === 'line' || klass === 'area') ? (overrideGeometry || klass) : klass;
64546                     });
64547
64548                 // pick at most one primary classification tag..
64549                 for (i = 0; i < primaries.length; i++) {
64550                     k = primaries[i];
64551                     v = t[k];
64552                     if (!v || v === 'no') continue;
64553
64554                     if (k === 'piste:type') {  // avoid a ':' in the class name
64555                         k = 'piste';
64556                     } else if (k === 'building:part') {  // avoid a ':' in the class name
64557                         k = 'building_part';
64558                     }
64559
64560                     primary = k;
64561                     if (statuses.indexOf(v) !== -1) {   // e.g. `railway=abandoned`
64562                         status = v;
64563                         classes.push('tag-' + k);
64564                     } else {
64565                         classes.push('tag-' + k);
64566                         classes.push('tag-' + k + '-' + v);
64567                     }
64568
64569                     break;
64570                 }
64571
64572                 if (!primary) {
64573                     for (i = 0; i < statuses.length; i++) {
64574                         for (j = 0; j < primaries.length; j++) {
64575                             k = statuses[i] + ':' + primaries[j];  // e.g. `demolished:building=yes`
64576                             v = t[k];
64577                             if (!v || v === 'no') continue;
64578
64579                             status = statuses[i];
64580                             break;
64581                         }
64582                     }
64583                 }
64584
64585                 // add at most one status tag, only if relates to primary tag..
64586                 if (!status) {
64587                     for (i = 0; i < statuses.length; i++) {
64588                         k = statuses[i];
64589                         v = t[k];
64590                         if (!v || v === 'no') continue;
64591
64592                         if (v === 'yes') {   // e.g. `railway=rail + abandoned=yes`
64593                             status = k;
64594                         }
64595                         else if (primary && primary === v) {  // e.g. `railway=rail + abandoned=railway`
64596                             status = k;
64597                         } else if (!primary && primaries.indexOf(v) !== -1) {  // e.g. `abandoned=railway`
64598                             status = k;
64599                             primary = v;
64600                             classes.push('tag-' + v);
64601                         }  // else ignore e.g.  `highway=path + abandoned=railway`
64602
64603                         if (status) break;
64604                     }
64605                 }
64606
64607                 if (status) {
64608                     classes.push('tag-status');
64609                     classes.push('tag-status-' + status);
64610                 }
64611
64612                 // add any secondary tags
64613                 for (i = 0; i < secondaries.length; i++) {
64614                     k = secondaries[i];
64615                     v = t[k];
64616                     if (!v || v === 'no' || k === primary) continue;
64617                     classes.push('tag-' + k);
64618                     classes.push('tag-' + k + '-' + v);
64619                 }
64620
64621                 // For highways, look for surface tagging..
64622                 if ((primary === 'highway' && !osmPathHighwayTagValues[t.highway]) || primary === 'aeroway') {
64623                     var surface = t.highway === 'track' ? 'unpaved' : 'paved';
64624                     for (k in t) {
64625                         v = t[k];
64626                         if (k in osmPavedTags) {
64627                             surface = osmPavedTags[k][v] ? 'paved' : 'unpaved';
64628                         }
64629                         if (k in osmSemipavedTags && !!osmSemipavedTags[k][v]) {
64630                             surface = 'semipaved';
64631                         }
64632                     }
64633                     classes.push('tag-' + surface);
64634                 }
64635
64636                 // If this is a wikidata-tagged item, add a class for that..
64637                 if (t.wikidata || t['brand:wikidata']) {
64638                     classes.push('tag-wikidata');
64639                 }
64640
64641                 return classes.join(' ').trim();
64642             };
64643
64644
64645             tagClasses.tags = function(val) {
64646                 if (!arguments.length) return _tags;
64647                 _tags = val;
64648                 return tagClasses;
64649             };
64650
64651             return tagClasses;
64652         }
64653
64654         // Patterns only work in Firefox when set directly on element.
64655         // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
64656         var patterns = {
64657             // tag - pattern name
64658             // -or-
64659             // tag - value - pattern name
64660             // -or-
64661             // tag - value - rules (optional tag-values, pattern name)
64662             // (matches earlier rules first, so fallback should be last entry)
64663             amenity: {
64664                 grave_yard: 'cemetery',
64665                 fountain: 'water_standing'
64666             },
64667             landuse: {
64668                 cemetery: [
64669                     { religion: 'christian', pattern: 'cemetery_christian' },
64670                     { religion: 'buddhist', pattern: 'cemetery_buddhist' },
64671                     { religion: 'muslim', pattern: 'cemetery_muslim' },
64672                     { religion: 'jewish', pattern: 'cemetery_jewish' },
64673                     { pattern: 'cemetery' }
64674                 ],
64675                 construction: 'construction',
64676                 farmland: 'farmland',
64677                 farmyard: 'farmyard',
64678                 forest: [
64679                     { leaf_type: 'broadleaved', pattern: 'forest_broadleaved' },
64680                     { leaf_type: 'needleleaved', pattern: 'forest_needleleaved' },
64681                     { leaf_type: 'leafless', pattern: 'forest_leafless' },
64682                     { pattern: 'forest' } // same as 'leaf_type:mixed'
64683                 ],
64684                 grave_yard: 'cemetery',
64685                 grass: [
64686                     { golf: 'green', pattern: 'golf_green' },
64687                     { pattern: 'grass' },
64688                 ],
64689                 landfill: 'landfill',
64690                 meadow: 'meadow',
64691                 military: 'construction',
64692                 orchard: 'orchard',
64693                 quarry: 'quarry',
64694                 vineyard: 'vineyard'
64695             },
64696             natural: {
64697                 beach: 'beach',
64698                 grassland: 'grass',
64699                 sand: 'beach',
64700                 scrub: 'scrub',
64701                 water: [
64702                     { water: 'pond', pattern: 'pond' },
64703                     { water: 'reservoir', pattern: 'water_standing' },
64704                     { pattern: 'waves' }
64705                 ],
64706                 wetland: [
64707                     { wetland: 'marsh', pattern: 'wetland_marsh' },
64708                     { wetland: 'swamp', pattern: 'wetland_swamp' },
64709                     { wetland: 'bog', pattern: 'wetland_bog' },
64710                     { wetland: 'reedbed', pattern: 'wetland_reedbed' },
64711                     { pattern: 'wetland' }
64712                 ],
64713                 wood: [
64714                     { leaf_type: 'broadleaved', pattern: 'forest_broadleaved' },
64715                     { leaf_type: 'needleleaved', pattern: 'forest_needleleaved' },
64716                     { leaf_type: 'leafless', pattern: 'forest_leafless' },
64717                     { pattern: 'forest' } // same as 'leaf_type:mixed'
64718                 ]
64719             },
64720             traffic_calming: {
64721                 island: [
64722                     { surface: 'grass', pattern: 'grass' },
64723                 ],
64724                 chicane: [
64725                     { surface: 'grass', pattern: 'grass' },
64726                 ],
64727                 choker: [
64728                     { surface: 'grass', pattern: 'grass' },
64729                 ]
64730             }
64731         };
64732
64733         function svgTagPattern(tags) {
64734             // Skip pattern filling if this is a building (buildings don't get patterns applied)
64735             if (tags.building && tags.building !== 'no') {
64736                 return null;
64737             }
64738
64739             for (var tag in patterns) {
64740                 var entityValue = tags[tag];
64741                 if (!entityValue) continue;
64742
64743                 if (typeof patterns[tag] === 'string') { // extra short syntax (just tag) - pattern name
64744                     return 'pattern-' + patterns[tag];
64745                 } else {
64746                     var values = patterns[tag];
64747                     for (var value in values) {
64748                         if (entityValue !== value) continue;
64749
64750                         var rules = values[value];
64751                         if (typeof rules === 'string') { // short syntax - pattern name
64752                             return 'pattern-' + rules;
64753                         }
64754
64755                         // long syntax - rule array
64756                         for (var ruleKey in rules) {
64757                             var rule = rules[ruleKey];
64758
64759                             var pass = true;
64760                             for (var criterion in rule) {
64761                                 if (criterion !== 'pattern') { // reserved for pattern name
64762                                     // The only rule is a required tag-value pair
64763                                     var v = tags[criterion];
64764                                     if (!v || v !== rule[criterion]) {
64765                                         pass = false;
64766                                         break;
64767                                     }
64768                                 }
64769                             }
64770
64771                             if (pass) {
64772                                 return 'pattern-' + rule.pattern;
64773                             }
64774                         }
64775                     }
64776                 }
64777             }
64778
64779             return null;
64780         }
64781
64782         function svgAreas(projection, context) {
64783
64784
64785             function getPatternStyle(tags) {
64786                 var imageID = svgTagPattern(tags);
64787                 if (imageID) {
64788                     return 'url("#ideditor-' + imageID + '")';
64789                 }
64790                 return '';
64791             }
64792
64793
64794             function drawTargets(selection, graph, entities, filter) {
64795                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
64796                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
64797                 var getPath = svgPath(projection).geojson;
64798                 var activeID = context.activeID();
64799                 var base = context.history().base();
64800
64801                 // The targets and nopes will be MultiLineString sub-segments of the ways
64802                 var data = { targets: [], nopes: [] };
64803
64804                 entities.forEach(function(way) {
64805                     var features = svgSegmentWay(way, graph, activeID);
64806                     data.targets.push.apply(data.targets, features.passive);
64807                     data.nopes.push.apply(data.nopes, features.active);
64808                 });
64809
64810
64811                 // Targets allow hover and vertex snapping
64812                 var targetData = data.targets.filter(getPath);
64813                 var targets = selection.selectAll('.area.target-allowed')
64814                     .filter(function(d) { return filter(d.properties.entity); })
64815                     .data(targetData, function key(d) { return d.id; });
64816
64817                 // exit
64818                 targets.exit()
64819                     .remove();
64820
64821                 var segmentWasEdited = function(d) {
64822                     var wayID = d.properties.entity.id;
64823                     // if the whole line was edited, don't draw segment changes
64824                     if (!base.entities[wayID] ||
64825                         !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
64826                         return false;
64827                     }
64828                     return d.properties.nodes.some(function(n) {
64829                         return !base.entities[n.id] ||
64830                                !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
64831                     });
64832                 };
64833
64834                 // enter/update
64835                 targets.enter()
64836                     .append('path')
64837                     .merge(targets)
64838                     .attr('d', getPath)
64839                     .attr('class', function(d) { return 'way area target target-allowed ' + targetClass + d.id; })
64840                     .classed('segment-edited', segmentWasEdited);
64841
64842
64843                 // NOPE
64844                 var nopeData = data.nopes.filter(getPath);
64845                 var nopes = selection.selectAll('.area.target-nope')
64846                     .filter(function(d) { return filter(d.properties.entity); })
64847                     .data(nopeData, function key(d) { return d.id; });
64848
64849                 // exit
64850                 nopes.exit()
64851                     .remove();
64852
64853                 // enter/update
64854                 nopes.enter()
64855                     .append('path')
64856                     .merge(nopes)
64857                     .attr('d', getPath)
64858                     .attr('class', function(d) { return 'way area target target-nope ' + nopeClass + d.id; })
64859                     .classed('segment-edited', segmentWasEdited);
64860             }
64861
64862
64863             function drawAreas(selection, graph, entities, filter) {
64864                 var path = svgPath(projection, graph, true);
64865                 var areas = {};
64866                 var multipolygon;
64867                 var base = context.history().base();
64868
64869                 for (var i = 0; i < entities.length; i++) {
64870                     var entity = entities[i];
64871                     if (entity.geometry(graph) !== 'area') continue;
64872
64873                     multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
64874                     if (multipolygon) {
64875                         areas[multipolygon.id] = {
64876                             entity: multipolygon.mergeTags(entity.tags),
64877                             area: Math.abs(entity.area(graph))
64878                         };
64879                     } else if (!areas[entity.id]) {
64880                         areas[entity.id] = {
64881                             entity: entity,
64882                             area: Math.abs(entity.area(graph))
64883                         };
64884                     }
64885                 }
64886
64887                 var fills = Object.values(areas).filter(function hasPath(a) { return path(a.entity); });
64888                 fills.sort(function areaSort(a, b) { return b.area - a.area; });
64889                 fills = fills.map(function(a) { return a.entity; });
64890
64891                 var strokes = fills.filter(function(area) { return area.type === 'way'; });
64892
64893                 var data = {
64894                     clip: fills,
64895                     shadow: strokes,
64896                     stroke: strokes,
64897                     fill: fills
64898                 };
64899
64900                 var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath-osm')
64901                    .filter(filter)
64902                    .data(data.clip, osmEntity.key);
64903
64904                 clipPaths.exit()
64905                    .remove();
64906
64907                 var clipPathsEnter = clipPaths.enter()
64908                    .append('clipPath')
64909                    .attr('class', 'clipPath-osm')
64910                    .attr('id', function(entity) { return 'ideditor-' + entity.id + '-clippath'; });
64911
64912                 clipPathsEnter
64913                    .append('path');
64914
64915                 clipPaths.merge(clipPathsEnter)
64916                    .selectAll('path')
64917                    .attr('d', path);
64918
64919
64920                 var drawLayer = selection.selectAll('.layer-osm.areas');
64921                 var touchLayer = selection.selectAll('.layer-touch.areas');
64922
64923                 // Draw areas..
64924                 var areagroup = drawLayer
64925                     .selectAll('g.areagroup')
64926                     .data(['fill', 'shadow', 'stroke']);
64927
64928                 areagroup = areagroup.enter()
64929                     .append('g')
64930                     .attr('class', function(d) { return 'areagroup area-' + d; })
64931                     .merge(areagroup);
64932
64933                 var paths = areagroup
64934                     .selectAll('path')
64935                     .filter(filter)
64936                     .data(function(layer) { return data[layer]; }, osmEntity.key);
64937
64938                 paths.exit()
64939                     .remove();
64940
64941
64942                 var fillpaths = selection.selectAll('.area-fill path.area').nodes();
64943                 var bisect = d3_bisector(function(node) { return -node.__data__.area(graph); }).left;
64944
64945                 function sortedByArea(entity) {
64946                     if (this._parent.__data__ === 'fill') {
64947                         return fillpaths[bisect(fillpaths, -entity.area(graph))];
64948                     }
64949                 }
64950
64951                 paths = paths.enter()
64952                     .insert('path', sortedByArea)
64953                     .merge(paths)
64954                     .each(function(entity) {
64955                         var layer = this.parentNode.__data__;
64956                         this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
64957
64958                         if (layer === 'fill') {
64959                             this.setAttribute('clip-path', 'url(#ideditor-' + entity.id + '-clippath)');
64960                             this.style.fill = this.style.stroke = getPatternStyle(entity.tags);
64961                         }
64962                     })
64963                     .classed('added', function(d) {
64964                         return !base.entities[d.id];
64965                     })
64966                     .classed('geometry-edited', function(d) {
64967                         return graph.entities[d.id] &&
64968                             base.entities[d.id] &&
64969                             !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
64970                     })
64971                     .classed('retagged', function(d) {
64972                         return graph.entities[d.id] &&
64973                             base.entities[d.id] &&
64974                             !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
64975                     })
64976                     .call(svgTagClasses())
64977                     .attr('d', path);
64978
64979
64980                 // Draw touch targets..
64981                 touchLayer
64982                     .call(drawTargets, graph, data.stroke, filter);
64983             }
64984
64985             return drawAreas;
64986         }
64987
64988         //[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]
64989         //[4a]          NameChar           ::=          NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
64990         //[5]           Name       ::=          NameStartChar (NameChar)*
64991         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
64992         var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]");
64993         var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$');
64994         //var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
64995         //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(',')
64996
64997         //S_TAG,        S_ATTR, S_EQ,   S_ATTR_NOQUOT_VALUE
64998         //S_ATTR_SPACE, S_ATTR_END,     S_TAG_SPACE, S_TAG_CLOSE
64999         var S_TAG = 0;//tag name offerring
65000         var S_ATTR = 1;//attr name offerring 
65001         var S_ATTR_SPACE=2;//attr name end and space offer
65002         var S_EQ = 3;//=space?
65003         var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only)
65004         var S_ATTR_END = 5;//attr value end and no space(quot end)
65005         var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer)
65006         var S_TAG_CLOSE = 7;//closed el<el />
65007
65008         function XMLReader(){
65009                 
65010         }
65011
65012         XMLReader.prototype = {
65013                 parse:function(source,defaultNSMap,entityMap){
65014                         var domBuilder = this.domBuilder;
65015                         domBuilder.startDocument();
65016                         _copy(defaultNSMap ,defaultNSMap = {});
65017                         parse(source,defaultNSMap,entityMap,
65018                                         domBuilder,this.errorHandler);
65019                         domBuilder.endDocument();
65020                 }
65021         };
65022         function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){
65023                 function fixedFromCharCode(code) {
65024                         // String.prototype.fromCharCode does not supports
65025                         // > 2 bytes unicode chars directly
65026                         if (code > 0xffff) {
65027                                 code -= 0x10000;
65028                                 var surrogate1 = 0xd800 + (code >> 10)
65029                                         , surrogate2 = 0xdc00 + (code & 0x3ff);
65030
65031                                 return String.fromCharCode(surrogate1, surrogate2);
65032                         } else {
65033                                 return String.fromCharCode(code);
65034                         }
65035                 }
65036                 function entityReplacer(a){
65037                         var k = a.slice(1,-1);
65038                         if(k in entityMap){
65039                                 return entityMap[k]; 
65040                         }else if(k.charAt(0) === '#'){
65041                                 return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x')))
65042                         }else {
65043                                 errorHandler.error('entity not found:'+a);
65044                                 return a;
65045                         }
65046                 }
65047                 function appendText(end){//has some bugs
65048                         if(end>start){
65049                                 var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer);
65050                                 locator&&position(start);
65051                                 domBuilder.characters(xt,0,end-start);
65052                                 start = end;
65053                         }
65054                 }
65055                 function position(p,m){
65056                         while(p>=lineEnd && (m = linePattern.exec(source))){
65057                                 lineStart = m.index;
65058                                 lineEnd = lineStart + m[0].length;
65059                                 locator.lineNumber++;
65060                                 //console.log('line++:',locator,startPos,endPos)
65061                         }
65062                         locator.columnNumber = p-lineStart+1;
65063                 }
65064                 var lineStart = 0;
65065                 var lineEnd = 0;
65066                 var linePattern = /.*(?:\r\n?|\n)|.*$/g;
65067                 var locator = domBuilder.locator;
65068                 
65069                 var parseStack = [{currentNSMap:defaultNSMapCopy}];
65070                 var closeMap = {};
65071                 var start = 0;
65072                 while(true){
65073                         try{
65074                                 var tagStart = source.indexOf('<',start);
65075                                 if(tagStart<0){
65076                                         if(!source.substr(start).match(/^\s*$/)){
65077                                                 var doc = domBuilder.doc;
65078                                         var text = doc.createTextNode(source.substr(start));
65079                                         doc.appendChild(text);
65080                                         domBuilder.currentElement = text;
65081                                         }
65082                                         return;
65083                                 }
65084                                 if(tagStart>start){
65085                                         appendText(tagStart);
65086                                 }
65087                                 switch(source.charAt(tagStart+1)){
65088                                 case '/':
65089                                         var end = source.indexOf('>',tagStart+3);
65090                                         var tagName = source.substring(tagStart+2,end);
65091                                         var config = parseStack.pop();
65092                                         if(end<0){
65093                                                 
65094                                         tagName = source.substring(tagStart+2).replace(/[\s<].*/,'');
65095                                         //console.error('#@@@@@@'+tagName)
65096                                         errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName);
65097                                         end = tagStart+1+tagName.length;
65098                                 }else if(tagName.match(/\s</)){
65099                                         tagName = tagName.replace(/[\s<].*/,'');
65100                                         errorHandler.error("end tag name: "+tagName+' maybe not complete');
65101                                         end = tagStart+1+tagName.length;
65102                                         }
65103                                         //console.error(parseStack.length,parseStack)
65104                                         //console.error(config);
65105                                         var localNSMap = config.localNSMap;
65106                                         var endMatch = config.tagName == tagName;
65107                                         var endIgnoreCaseMach = endMatch || config.tagName&&config.tagName.toLowerCase() == tagName.toLowerCase();
65108                                 if(endIgnoreCaseMach){
65109                                         domBuilder.endElement(config.uri,config.localName,tagName);
65110                                                 if(localNSMap){
65111                                                         for(var prefix in localNSMap){
65112                                                                 domBuilder.endPrefixMapping(prefix) ;
65113                                                         }
65114                                                 }
65115                                                 if(!endMatch){
65116                                         errorHandler.fatalError("end tag name: "+tagName+' is not match the current start tagName:'+config.tagName );
65117                                                 }
65118                                 }else {
65119                                         parseStack.push(config);
65120                                 }
65121                                         
65122                                         end++;
65123                                         break;
65124                                         // end elment
65125                                 case '?':// <?...?>
65126                                         locator&&position(tagStart);
65127                                         end = parseInstruction(source,tagStart,domBuilder);
65128                                         break;
65129                                 case '!':// <!doctype,<![CDATA,<!--
65130                                         locator&&position(tagStart);
65131                                         end = parseDCC(source,tagStart,domBuilder,errorHandler);
65132                                         break;
65133                                 default:
65134                                         locator&&position(tagStart);
65135                                         var el = new ElementAttributes();
65136                                         var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
65137                                         //elStartEnd
65138                                         var end = parseElementStartPart(source,tagStart,el,currentNSMap,entityReplacer,errorHandler);
65139                                         var len = el.length;
65140                                         
65141                                         
65142                                         if(!el.closed && fixSelfClosed(source,end,el.tagName,closeMap)){
65143                                                 el.closed = true;
65144                                                 if(!entityMap.nbsp){
65145                                                         errorHandler.warning('unclosed xml attribute');
65146                                                 }
65147                                         }
65148                                         if(locator && len){
65149                                                 var locator2 = copyLocator(locator,{});
65150                                                 //try{//attribute position fixed
65151                                                 for(var i = 0;i<len;i++){
65152                                                         var a = el[i];
65153                                                         position(a.offset);
65154                                                         a.locator = copyLocator(locator,{});
65155                                                 }
65156                                                 //}catch(e){console.error('@@@@@'+e)}
65157                                                 domBuilder.locator = locator2;
65158                                                 if(appendElement(el,domBuilder,currentNSMap)){
65159                                                         parseStack.push(el);
65160                                                 }
65161                                                 domBuilder.locator = locator;
65162                                         }else {
65163                                                 if(appendElement(el,domBuilder,currentNSMap)){
65164                                                         parseStack.push(el);
65165                                                 }
65166                                         }
65167                                         
65168                                         
65169                                         
65170                                         if(el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed){
65171                                                 end = parseHtmlSpecialContent(source,end,el.tagName,entityReplacer,domBuilder);
65172                                         }else {
65173                                                 end++;
65174                                         }
65175                                 }
65176                         }catch(e){
65177                                 errorHandler.error('element parse error: '+e);
65178                                 //errorHandler.error('element parse error: '+e);
65179                                 end = -1;
65180                                 //throw e;
65181                         }
65182                         if(end>start){
65183                                 start = end;
65184                         }else {
65185                                 //TODO: 这里有可能sax回退,有位置错误风险
65186                                 appendText(Math.max(tagStart,start)+1);
65187                         }
65188                 }
65189         }
65190         function copyLocator(f,t){
65191                 t.lineNumber = f.lineNumber;
65192                 t.columnNumber = f.columnNumber;
65193                 return t;
65194         }
65195
65196         /**
65197          * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
65198          * @return end of the elementStartPart(end of elementEndPart for selfClosed el)
65199          */
65200         function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){
65201                 var attrName;
65202                 var value;
65203                 var p = ++start;
65204                 var s = S_TAG;//status
65205                 while(true){
65206                         var c = source.charAt(p);
65207                         switch(c){
65208                         case '=':
65209                                 if(s === S_ATTR){//attrName
65210                                         attrName = source.slice(start,p);
65211                                         s = S_EQ;
65212                                 }else if(s === S_ATTR_SPACE){
65213                                         s = S_EQ;
65214                                 }else {
65215                                         //fatalError: equal must after attrName or space after attrName
65216                                         throw new Error('attribute equal must after attrName');
65217                                 }
65218                                 break;
65219                         case '\'':
65220                         case '"':
65221                                 if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE
65222                                         ){//equal
65223                                         if(s === S_ATTR){
65224                                                 errorHandler.warning('attribute value must after "="');
65225                                                 attrName = source.slice(start,p);
65226                                         }
65227                                         start = p+1;
65228                                         p = source.indexOf(c,start);
65229                                         if(p>0){
65230                                                 value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65231                                                 el.add(attrName,value,start-1);
65232                                                 s = S_ATTR_END;
65233                                         }else {
65234                                                 //fatalError: no end quot match
65235                                                 throw new Error('attribute value no end \''+c+'\' match');
65236                                         }
65237                                 }else if(s == S_ATTR_NOQUOT_VALUE){
65238                                         value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65239                                         //console.log(attrName,value,start,p)
65240                                         el.add(attrName,value,start);
65241                                         //console.dir(el)
65242                                         errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!');
65243                                         start = p+1;
65244                                         s = S_ATTR_END;
65245                                 }else {
65246                                         //fatalError: no equal before
65247                                         throw new Error('attribute value must after "="');
65248                                 }
65249                                 break;
65250                         case '/':
65251                                 switch(s){
65252                                 case S_TAG:
65253                                         el.setTagName(source.slice(start,p));
65254                                 case S_ATTR_END:
65255                                 case S_TAG_SPACE:
65256                                 case S_TAG_CLOSE:
65257                                         s =S_TAG_CLOSE;
65258                                         el.closed = true;
65259                                 case S_ATTR_NOQUOT_VALUE:
65260                                 case S_ATTR:
65261                                 case S_ATTR_SPACE:
65262                                         break;
65263                                 //case S_EQ:
65264                                 default:
65265                                         throw new Error("attribute invalid close char('/')")
65266                                 }
65267                                 break;
65268                         case ''://end document
65269                                 //throw new Error('unexpected end of input')
65270                                 errorHandler.error('unexpected end of input');
65271                                 if(s == S_TAG){
65272                                         el.setTagName(source.slice(start,p));
65273                                 }
65274                                 return p;
65275                         case '>':
65276                                 switch(s){
65277                                 case S_TAG:
65278                                         el.setTagName(source.slice(start,p));
65279                                 case S_ATTR_END:
65280                                 case S_TAG_SPACE:
65281                                 case S_TAG_CLOSE:
65282                                         break;//normal
65283                                 case S_ATTR_NOQUOT_VALUE://Compatible state
65284                                 case S_ATTR:
65285                                         value = source.slice(start,p);
65286                                         if(value.slice(-1) === '/'){
65287                                                 el.closed  = true;
65288                                                 value = value.slice(0,-1);
65289                                         }
65290                                 case S_ATTR_SPACE:
65291                                         if(s === S_ATTR_SPACE){
65292                                                 value = attrName;
65293                                         }
65294                                         if(s == S_ATTR_NOQUOT_VALUE){
65295                                                 errorHandler.warning('attribute "'+value+'" missed quot(")!!');
65296                                                 el.add(attrName,value.replace(/&#?\w+;/g,entityReplacer),start);
65297                                         }else {
65298                                                 if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)){
65299                                                         errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!');
65300                                                 }
65301                                                 el.add(value,value,start);
65302                                         }
65303                                         break;
65304                                 case S_EQ:
65305                                         throw new Error('attribute value missed!!');
65306                                 }
65307         //                      console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
65308                                 return p;
65309                         /*xml space '\x20' | #x9 | #xD | #xA; */
65310                         case '\u0080':
65311                                 c = ' ';
65312                         default:
65313                                 if(c<= ' '){//space
65314                                         switch(s){
65315                                         case S_TAG:
65316                                                 el.setTagName(source.slice(start,p));//tagName
65317                                                 s = S_TAG_SPACE;
65318                                                 break;
65319                                         case S_ATTR:
65320                                                 attrName = source.slice(start,p);
65321                                                 s = S_ATTR_SPACE;
65322                                                 break;
65323                                         case S_ATTR_NOQUOT_VALUE:
65324                                                 var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65325                                                 errorHandler.warning('attribute "'+value+'" missed quot(")!!');
65326                                                 el.add(attrName,value,start);
65327                                         case S_ATTR_END:
65328                                                 s = S_TAG_SPACE;
65329                                                 break;
65330                                         //case S_TAG_SPACE:
65331                                         //case S_EQ:
65332                                         //case S_ATTR_SPACE:
65333                                         //      void();break;
65334                                         //case S_TAG_CLOSE:
65335                                                 //ignore warning
65336                                         }
65337                                 }else {//not space
65338         //S_TAG,        S_ATTR, S_EQ,   S_ATTR_NOQUOT_VALUE
65339         //S_ATTR_SPACE, S_ATTR_END,     S_TAG_SPACE, S_TAG_CLOSE
65340                                         switch(s){
65341                                         //case S_TAG:void();break;
65342                                         //case S_ATTR:void();break;
65343                                         //case S_ATTR_NOQUOT_VALUE:void();break;
65344                                         case S_ATTR_SPACE:
65345                                                 var tagName =  el.tagName;
65346                                                 if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)){
65347                                                         errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!');
65348                                                 }
65349                                                 el.add(attrName,attrName,start);
65350                                                 start = p;
65351                                                 s = S_ATTR;
65352                                                 break;
65353                                         case S_ATTR_END:
65354                                                 errorHandler.warning('attribute space is required"'+attrName+'"!!');
65355                                         case S_TAG_SPACE:
65356                                                 s = S_ATTR;
65357                                                 start = p;
65358                                                 break;
65359                                         case S_EQ:
65360                                                 s = S_ATTR_NOQUOT_VALUE;
65361                                                 start = p;
65362                                                 break;
65363                                         case S_TAG_CLOSE:
65364                                                 throw new Error("elements closed character '/' and '>' must be connected to");
65365                                         }
65366                                 }
65367                         }//end outer switch
65368                         //console.log('p++',p)
65369                         p++;
65370                 }
65371         }
65372         /**
65373          * @return true if has new namespace define
65374          */
65375         function appendElement(el,domBuilder,currentNSMap){
65376                 var tagName = el.tagName;
65377                 var localNSMap = null;
65378                 //var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
65379                 var i = el.length;
65380                 while(i--){
65381                         var a = el[i];
65382                         var qName = a.qName;
65383                         var value = a.value;
65384                         var nsp = qName.indexOf(':');
65385                         if(nsp>0){
65386                                 var prefix = a.prefix = qName.slice(0,nsp);
65387                                 var localName = qName.slice(nsp+1);
65388                                 var nsPrefix = prefix === 'xmlns' && localName;
65389                         }else {
65390                                 localName = qName;
65391                                 prefix = null;
65392                                 nsPrefix = qName === 'xmlns' && '';
65393                         }
65394                         //can not set prefix,because prefix !== ''
65395                         a.localName = localName ;
65396                         //prefix == null for no ns prefix attribute 
65397                         if(nsPrefix !== false){//hack!!
65398                                 if(localNSMap == null){
65399                                         localNSMap = {};
65400                                         //console.log(currentNSMap,0)
65401                                         _copy(currentNSMap,currentNSMap={});
65402                                         //console.log(currentNSMap,1)
65403                                 }
65404                                 currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
65405                                 a.uri = 'http://www.w3.org/2000/xmlns/';
65406                                 domBuilder.startPrefixMapping(nsPrefix, value); 
65407                         }
65408                 }
65409                 var i = el.length;
65410                 while(i--){
65411                         a = el[i];
65412                         var prefix = a.prefix;
65413                         if(prefix){//no prefix attribute has no namespace
65414                                 if(prefix === 'xml'){
65415                                         a.uri = 'http://www.w3.org/XML/1998/namespace';
65416                                 }if(prefix !== 'xmlns'){
65417                                         a.uri = currentNSMap[prefix || ''];
65418                                         
65419                                         //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
65420                                 }
65421                         }
65422                 }
65423                 var nsp = tagName.indexOf(':');
65424                 if(nsp>0){
65425                         prefix = el.prefix = tagName.slice(0,nsp);
65426                         localName = el.localName = tagName.slice(nsp+1);
65427                 }else {
65428                         prefix = null;//important!!
65429                         localName = el.localName = tagName;
65430                 }
65431                 //no prefix element has default namespace
65432                 var ns = el.uri = currentNSMap[prefix || ''];
65433                 domBuilder.startElement(ns,localName,tagName,el);
65434                 //endPrefixMapping and startPrefixMapping have not any help for dom builder
65435                 //localNSMap = null
65436                 if(el.closed){
65437                         domBuilder.endElement(ns,localName,tagName);
65438                         if(localNSMap){
65439                                 for(prefix in localNSMap){
65440                                         domBuilder.endPrefixMapping(prefix); 
65441                                 }
65442                         }
65443                 }else {
65444                         el.currentNSMap = currentNSMap;
65445                         el.localNSMap = localNSMap;
65446                         //parseStack.push(el);
65447                         return true;
65448                 }
65449         }
65450         function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){
65451                 if(/^(?:script|textarea)$/i.test(tagName)){
65452                         var elEndStart =  source.indexOf('</'+tagName+'>',elStartEnd);
65453                         var text = source.substring(elStartEnd+1,elEndStart);
65454                         if(/[&<]/.test(text)){
65455                                 if(/^script$/i.test(tagName)){
65456                                         //if(!/\]\]>/.test(text)){
65457                                                 //lexHandler.startCDATA();
65458                                                 domBuilder.characters(text,0,text.length);
65459                                                 //lexHandler.endCDATA();
65460                                                 return elEndStart;
65461                                         //}
65462                                 }//}else{//text area
65463                                         text = text.replace(/&#?\w+;/g,entityReplacer);
65464                                         domBuilder.characters(text,0,text.length);
65465                                         return elEndStart;
65466                                 //}
65467                                 
65468                         }
65469                 }
65470                 return elStartEnd+1;
65471         }
65472         function fixSelfClosed(source,elStartEnd,tagName,closeMap){
65473                 //if(tagName in closeMap){
65474                 var pos = closeMap[tagName];
65475                 if(pos == null){
65476                         //console.log(tagName)
65477                         pos =  source.lastIndexOf('</'+tagName+'>');
65478                         if(pos<elStartEnd){//忘记闭合
65479                                 pos = source.lastIndexOf('</'+tagName);
65480                         }
65481                         closeMap[tagName] =pos;
65482                 }
65483                 return pos<elStartEnd;
65484                 //} 
65485         }
65486         function _copy(source,target){
65487                 for(var n in source){target[n] = source[n];}
65488         }
65489         function parseDCC(source,start,domBuilder,errorHandler){//sure start with '<!'
65490                 var next= source.charAt(start+2);
65491                 switch(next){
65492                 case '-':
65493                         if(source.charAt(start + 3) === '-'){
65494                                 var end = source.indexOf('-->',start+4);
65495                                 //append comment source.substring(4,end)//<!--
65496                                 if(end>start){
65497                                         domBuilder.comment(source,start+4,end-start-4);
65498                                         return end+3;
65499                                 }else {
65500                                         errorHandler.error("Unclosed comment");
65501                                         return -1;
65502                                 }
65503                         }else {
65504                                 //error
65505                                 return -1;
65506                         }
65507                 default:
65508                         if(source.substr(start+3,6) == 'CDATA['){
65509                                 var end = source.indexOf(']]>',start+9);
65510                                 domBuilder.startCDATA();
65511                                 domBuilder.characters(source,start+9,end-start-9);
65512                                 domBuilder.endCDATA(); 
65513                                 return end+3;
65514                         }
65515                         //<!DOCTYPE
65516                         //startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId) 
65517                         var matchs = split(source,start);
65518                         var len = matchs.length;
65519                         if(len>1 && /!doctype/i.test(matchs[0][0])){
65520                                 var name = matchs[1][0];
65521                                 var pubid = len>3 && /^public$/i.test(matchs[2][0]) && matchs[3][0];
65522                                 var sysid = len>4 && matchs[4][0];
65523                                 var lastMatch = matchs[len-1];
65524                                 domBuilder.startDTD(name,pubid && pubid.replace(/^(['"])(.*?)\1$/,'$2'),
65525                                                 sysid && sysid.replace(/^(['"])(.*?)\1$/,'$2'));
65526                                 domBuilder.endDTD();
65527                                 
65528                                 return lastMatch.index+lastMatch[0].length
65529                         }
65530                 }
65531                 return -1;
65532         }
65533
65534
65535
65536         function parseInstruction(source,start,domBuilder){
65537                 var end = source.indexOf('?>',start);
65538                 if(end){
65539                         var match = source.substring(start,end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
65540                         if(match){
65541                                 var len = match[0].length;
65542                                 domBuilder.processingInstruction(match[1], match[2]) ;
65543                                 return end+2;
65544                         }else {//error
65545                                 return -1;
65546                         }
65547                 }
65548                 return -1;
65549         }
65550
65551         /**
65552          * @param source
65553          */
65554         function ElementAttributes(source){
65555                 
65556         }
65557         ElementAttributes.prototype = {
65558                 setTagName:function(tagName){
65559                         if(!tagNamePattern.test(tagName)){
65560                                 throw new Error('invalid tagName:'+tagName)
65561                         }
65562                         this.tagName = tagName;
65563                 },
65564                 add:function(qName,value,offset){
65565                         if(!tagNamePattern.test(qName)){
65566                                 throw new Error('invalid attribute:'+qName)
65567                         }
65568                         this[this.length++] = {qName:qName,value:value,offset:offset};
65569                 },
65570                 length:0,
65571                 getLocalName:function(i){return this[i].localName},
65572                 getLocator:function(i){return this[i].locator},
65573                 getQName:function(i){return this[i].qName},
65574                 getURI:function(i){return this[i].uri},
65575                 getValue:function(i){return this[i].value}
65576         //      ,getIndex:function(uri, localName)){
65577         //              if(localName){
65578         //                      
65579         //              }else{
65580         //                      var qName = uri
65581         //              }
65582         //      },
65583         //      getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
65584         //      getType:function(uri,localName){}
65585         //      getType:function(i){},
65586         };
65587
65588
65589
65590
65591         function _set_proto_(thiz,parent){
65592                 thiz.__proto__ = parent;
65593                 return thiz;
65594         }
65595         if(!(_set_proto_({},_set_proto_.prototype) instanceof _set_proto_)){
65596                 _set_proto_ = function(thiz,parent){
65597                         function p(){}          p.prototype = parent;
65598                         p = new p();
65599                         for(parent in thiz){
65600                                 p[parent] = thiz[parent];
65601                         }
65602                         return p;
65603                 };
65604         }
65605
65606         function split(source,start){
65607                 var match;
65608                 var buf = [];
65609                 var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
65610                 reg.lastIndex = start;
65611                 reg.exec(source);//skip <
65612                 while(match = reg.exec(source)){
65613                         buf.push(match);
65614                         if(match[1])return buf;
65615                 }
65616         }
65617
65618         var XMLReader_1 = XMLReader;
65619
65620         var sax = {
65621                 XMLReader: XMLReader_1
65622         };
65623
65624         /*
65625          * DOM Level 2
65626          * Object DOMException
65627          * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
65628          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
65629          */
65630
65631         function copy$2(src,dest){
65632                 for(var p in src){
65633                         dest[p] = src[p];
65634                 }
65635         }
65636         /**
65637         ^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
65638         ^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
65639          */
65640         function _extends(Class,Super){
65641                 var pt = Class.prototype;
65642                 if(Object.create){
65643                         var ppt = Object.create(Super.prototype);
65644                         pt.__proto__ = ppt;
65645                 }
65646                 if(!(pt instanceof Super)){
65647                         function t(){}          t.prototype = Super.prototype;
65648                         t = new t();
65649                         copy$2(pt,t);
65650                         Class.prototype = pt = t;
65651                 }
65652                 if(pt.constructor != Class){
65653                         if(typeof Class != 'function'){
65654                                 console.error("unknow Class:"+Class);
65655                         }
65656                         pt.constructor = Class;
65657                 }
65658         }
65659         var htmlns = 'http://www.w3.org/1999/xhtml' ;
65660         // Node Types
65661         var NodeType = {};
65662         var ELEMENT_NODE                = NodeType.ELEMENT_NODE                = 1;
65663         var ATTRIBUTE_NODE              = NodeType.ATTRIBUTE_NODE              = 2;
65664         var TEXT_NODE                   = NodeType.TEXT_NODE                   = 3;
65665         var CDATA_SECTION_NODE          = NodeType.CDATA_SECTION_NODE          = 4;
65666         var ENTITY_REFERENCE_NODE       = NodeType.ENTITY_REFERENCE_NODE       = 5;
65667         var ENTITY_NODE                 = NodeType.ENTITY_NODE                 = 6;
65668         var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
65669         var COMMENT_NODE                = NodeType.COMMENT_NODE                = 8;
65670         var DOCUMENT_NODE               = NodeType.DOCUMENT_NODE               = 9;
65671         var DOCUMENT_TYPE_NODE          = NodeType.DOCUMENT_TYPE_NODE          = 10;
65672         var DOCUMENT_FRAGMENT_NODE      = NodeType.DOCUMENT_FRAGMENT_NODE      = 11;
65673         var NOTATION_NODE               = NodeType.NOTATION_NODE               = 12;
65674
65675         // ExceptionCode
65676         var ExceptionCode = {};
65677         var ExceptionMessage = {};
65678         var INDEX_SIZE_ERR              = ExceptionCode.INDEX_SIZE_ERR              = ((ExceptionMessage[1]="Index size error"),1);
65679         var DOMSTRING_SIZE_ERR          = ExceptionCode.DOMSTRING_SIZE_ERR          = ((ExceptionMessage[2]="DOMString size error"),2);
65680         var HIERARCHY_REQUEST_ERR       = ExceptionCode.HIERARCHY_REQUEST_ERR       = ((ExceptionMessage[3]="Hierarchy request error"),3);
65681         var WRONG_DOCUMENT_ERR          = ExceptionCode.WRONG_DOCUMENT_ERR          = ((ExceptionMessage[4]="Wrong document"),4);
65682         var INVALID_CHARACTER_ERR       = ExceptionCode.INVALID_CHARACTER_ERR       = ((ExceptionMessage[5]="Invalid character"),5);
65683         var NO_DATA_ALLOWED_ERR         = ExceptionCode.NO_DATA_ALLOWED_ERR         = ((ExceptionMessage[6]="No data allowed"),6);
65684         var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = ((ExceptionMessage[7]="No modification allowed"),7);
65685         var NOT_FOUND_ERR               = ExceptionCode.NOT_FOUND_ERR               = ((ExceptionMessage[8]="Not found"),8);
65686         var NOT_SUPPORTED_ERR           = ExceptionCode.NOT_SUPPORTED_ERR           = ((ExceptionMessage[9]="Not supported"),9);
65687         var INUSE_ATTRIBUTE_ERR         = ExceptionCode.INUSE_ATTRIBUTE_ERR         = ((ExceptionMessage[10]="Attribute in use"),10);
65688         //level2
65689         var INVALID_STATE_ERR           = ExceptionCode.INVALID_STATE_ERR               = ((ExceptionMessage[11]="Invalid state"),11);
65690         var SYNTAX_ERR                  = ExceptionCode.SYNTAX_ERR                      = ((ExceptionMessage[12]="Syntax error"),12);
65691         var INVALID_MODIFICATION_ERR    = ExceptionCode.INVALID_MODIFICATION_ERR        = ((ExceptionMessage[13]="Invalid modification"),13);
65692         var NAMESPACE_ERR               = ExceptionCode.NAMESPACE_ERR                   = ((ExceptionMessage[14]="Invalid namespace"),14);
65693         var INVALID_ACCESS_ERR          = ExceptionCode.INVALID_ACCESS_ERR              = ((ExceptionMessage[15]="Invalid access"),15);
65694
65695
65696         function DOMException$2(code, message) {
65697                 if(message instanceof Error){
65698                         var error = message;
65699                 }else {
65700                         error = this;
65701                         Error.call(this, ExceptionMessage[code]);
65702                         this.message = ExceptionMessage[code];
65703                         if(Error.captureStackTrace) Error.captureStackTrace(this, DOMException$2);
65704                 }
65705                 error.code = code;
65706                 if(message) this.message = this.message + ": " + message;
65707                 return error;
65708         }DOMException$2.prototype = Error.prototype;
65709         copy$2(ExceptionCode,DOMException$2);
65710         /**
65711          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
65712          * 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.
65713          * The items in the NodeList are accessible via an integral index, starting from 0.
65714          */
65715         function NodeList() {
65716         }NodeList.prototype = {
65717                 /**
65718                  * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
65719                  * @standard level1
65720                  */
65721                 length:0, 
65722                 /**
65723                  * 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.
65724                  * @standard level1
65725                  * @param index  unsigned long 
65726                  *   Index into the collection.
65727                  * @return Node
65728                  *      The node at the indexth position in the NodeList, or null if that is not a valid index. 
65729                  */
65730                 item: function(index) {
65731                         return this[index] || null;
65732                 },
65733                 toString:function(isHTML,nodeFilter){
65734                         for(var buf = [], i = 0;i<this.length;i++){
65735                                 serializeToString(this[i],buf,isHTML,nodeFilter);
65736                         }
65737                         return buf.join('');
65738                 }
65739         };
65740         function LiveNodeList(node,refresh){
65741                 this._node = node;
65742                 this._refresh = refresh;
65743                 _updateLiveList(this);
65744         }
65745         function _updateLiveList(list){
65746                 var inc = list._node._inc || list._node.ownerDocument._inc;
65747                 if(list._inc != inc){
65748                         var ls = list._refresh(list._node);
65749                         //console.log(ls.length)
65750                         __set__(list,'length',ls.length);
65751                         copy$2(ls,list);
65752                         list._inc = inc;
65753                 }
65754         }
65755         LiveNodeList.prototype.item = function(i){
65756                 _updateLiveList(this);
65757                 return this[i];
65758         };
65759
65760         _extends(LiveNodeList,NodeList);
65761         /**
65762          * 
65763          * 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.
65764          * NamedNodeMap objects in the DOM are live.
65765          * used for attributes or DocumentType entities 
65766          */
65767         function NamedNodeMap() {
65768         }
65769         function _findNodeIndex(list,node){
65770                 var i = list.length;
65771                 while(i--){
65772                         if(list[i] === node){return i}
65773                 }
65774         }
65775
65776         function _addNamedNode(el,list,newAttr,oldAttr){
65777                 if(oldAttr){
65778                         list[_findNodeIndex(list,oldAttr)] = newAttr;
65779                 }else {
65780                         list[list.length++] = newAttr;
65781                 }
65782                 if(el){
65783                         newAttr.ownerElement = el;
65784                         var doc = el.ownerDocument;
65785                         if(doc){
65786                                 oldAttr && _onRemoveAttribute(doc,el,oldAttr);
65787                                 _onAddAttribute(doc,el,newAttr);
65788                         }
65789                 }
65790         }
65791         function _removeNamedNode(el,list,attr){
65792                 //console.log('remove attr:'+attr)
65793                 var i = _findNodeIndex(list,attr);
65794                 if(i>=0){
65795                         var lastIndex = list.length-1;
65796                         while(i<lastIndex){
65797                                 list[i] = list[++i];
65798                         }
65799                         list.length = lastIndex;
65800                         if(el){
65801                                 var doc = el.ownerDocument;
65802                                 if(doc){
65803                                         _onRemoveAttribute(doc,el,attr);
65804                                         attr.ownerElement = null;
65805                                 }
65806                         }
65807                 }else {
65808                         throw DOMException$2(NOT_FOUND_ERR,new Error(el.tagName+'@'+attr))
65809                 }
65810         }
65811         NamedNodeMap.prototype = {
65812                 length:0,
65813                 item:NodeList.prototype.item,
65814                 getNamedItem: function(key) {
65815         //              if(key.indexOf(':')>0 || key == 'xmlns'){
65816         //                      return null;
65817         //              }
65818                         //console.log()
65819                         var i = this.length;
65820                         while(i--){
65821                                 var attr = this[i];
65822                                 //console.log(attr.nodeName,key)
65823                                 if(attr.nodeName == key){
65824                                         return attr;
65825                                 }
65826                         }
65827                 },
65828                 setNamedItem: function(attr) {
65829                         var el = attr.ownerElement;
65830                         if(el && el!=this._ownerElement){
65831                                 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
65832                         }
65833                         var oldAttr = this.getNamedItem(attr.nodeName);
65834                         _addNamedNode(this._ownerElement,this,attr,oldAttr);
65835                         return oldAttr;
65836                 },
65837                 /* returns Node */
65838                 setNamedItemNS: function(attr) {// raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR
65839                         var el = attr.ownerElement, oldAttr;
65840                         if(el && el!=this._ownerElement){
65841                                 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
65842                         }
65843                         oldAttr = this.getNamedItemNS(attr.namespaceURI,attr.localName);
65844                         _addNamedNode(this._ownerElement,this,attr,oldAttr);
65845                         return oldAttr;
65846                 },
65847
65848                 /* returns Node */
65849                 removeNamedItem: function(key) {
65850                         var attr = this.getNamedItem(key);
65851                         _removeNamedNode(this._ownerElement,this,attr);
65852                         return attr;
65853                         
65854                         
65855                 },// raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
65856                 
65857                 //for level2
65858                 removeNamedItemNS:function(namespaceURI,localName){
65859                         var attr = this.getNamedItemNS(namespaceURI,localName);
65860                         _removeNamedNode(this._ownerElement,this,attr);
65861                         return attr;
65862                 },
65863                 getNamedItemNS: function(namespaceURI, localName) {
65864                         var i = this.length;
65865                         while(i--){
65866                                 var node = this[i];
65867                                 if(node.localName == localName && node.namespaceURI == namespaceURI){
65868                                         return node;
65869                                 }
65870                         }
65871                         return null;
65872                 }
65873         };
65874         /**
65875          * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490
65876          */
65877         function DOMImplementation(/* Object */ features) {
65878                 this._features = {};
65879                 if (features) {
65880                         for (var feature in features) {
65881                                  this._features = features[feature];
65882                         }
65883                 }
65884         }
65885         DOMImplementation.prototype = {
65886                 hasFeature: function(/* string */ feature, /* string */ version) {
65887                         var versions = this._features[feature.toLowerCase()];
65888                         if (versions && (!version || version in versions)) {
65889                                 return true;
65890                         } else {
65891                                 return false;
65892                         }
65893                 },
65894                 // Introduced in DOM Level 2:
65895                 createDocument:function(namespaceURI,  qualifiedName, doctype){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR
65896                         var doc = new Document();
65897                         doc.implementation = this;
65898                         doc.childNodes = new NodeList();
65899                         doc.doctype = doctype;
65900                         if(doctype){
65901                                 doc.appendChild(doctype);
65902                         }
65903                         if(qualifiedName){
65904                                 var root = doc.createElementNS(namespaceURI,qualifiedName);
65905                                 doc.appendChild(root);
65906                         }
65907                         return doc;
65908                 },
65909                 // Introduced in DOM Level 2:
65910                 createDocumentType:function(qualifiedName, publicId, systemId){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
65911                         var node = new DocumentType();
65912                         node.name = qualifiedName;
65913                         node.nodeName = qualifiedName;
65914                         node.publicId = publicId;
65915                         node.systemId = systemId;
65916                         // Introduced in DOM Level 2:
65917                         //readonly attribute DOMString        internalSubset;
65918                         
65919                         //TODO:..
65920                         //  readonly attribute NamedNodeMap     entities;
65921                         //  readonly attribute NamedNodeMap     notations;
65922                         return node;
65923                 }
65924         };
65925
65926
65927         /**
65928          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
65929          */
65930
65931         function Node() {
65932         }
65933         Node.prototype = {
65934                 firstChild : null,
65935                 lastChild : null,
65936                 previousSibling : null,
65937                 nextSibling : null,
65938                 attributes : null,
65939                 parentNode : null,
65940                 childNodes : null,
65941                 ownerDocument : null,
65942                 nodeValue : null,
65943                 namespaceURI : null,
65944                 prefix : null,
65945                 localName : null,
65946                 // Modified in DOM Level 2:
65947                 insertBefore:function(newChild, refChild){//raises 
65948                         return _insertBefore(this,newChild,refChild);
65949                 },
65950                 replaceChild:function(newChild, oldChild){//raises 
65951                         this.insertBefore(newChild,oldChild);
65952                         if(oldChild){
65953                                 this.removeChild(oldChild);
65954                         }
65955                 },
65956                 removeChild:function(oldChild){
65957                         return _removeChild(this,oldChild);
65958                 },
65959                 appendChild:function(newChild){
65960                         return this.insertBefore(newChild,null);
65961                 },
65962                 hasChildNodes:function(){
65963                         return this.firstChild != null;
65964                 },
65965                 cloneNode:function(deep){
65966                         return cloneNode(this.ownerDocument||this,this,deep);
65967                 },
65968                 // Modified in DOM Level 2:
65969                 normalize:function(){
65970                         var child = this.firstChild;
65971                         while(child){
65972                                 var next = child.nextSibling;
65973                                 if(next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE){
65974                                         this.removeChild(next);
65975                                         child.appendData(next.data);
65976                                 }else {
65977                                         child.normalize();
65978                                         child = next;
65979                                 }
65980                         }
65981                 },
65982                 // Introduced in DOM Level 2:
65983                 isSupported:function(feature, version){
65984                         return this.ownerDocument.implementation.hasFeature(feature,version);
65985                 },
65986             // Introduced in DOM Level 2:
65987             hasAttributes:function(){
65988                 return this.attributes.length>0;
65989             },
65990             lookupPrefix:function(namespaceURI){
65991                 var el = this;
65992                 while(el){
65993                         var map = el._nsMap;
65994                         //console.dir(map)
65995                         if(map){
65996                                 for(var n in map){
65997                                         if(map[n] == namespaceURI){
65998                                                 return n;
65999                                         }
66000                                 }
66001                         }
66002                         el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
66003                 }
66004                 return null;
66005             },
66006             // Introduced in DOM Level 3:
66007             lookupNamespaceURI:function(prefix){
66008                 var el = this;
66009                 while(el){
66010                         var map = el._nsMap;
66011                         //console.dir(map)
66012                         if(map){
66013                                 if(prefix in map){
66014                                         return map[prefix] ;
66015                                 }
66016                         }
66017                         el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
66018                 }
66019                 return null;
66020             },
66021             // Introduced in DOM Level 3:
66022             isDefaultNamespace:function(namespaceURI){
66023                 var prefix = this.lookupPrefix(namespaceURI);
66024                 return prefix == null;
66025             }
66026         };
66027
66028
66029         function _xmlEncoder(c){
66030                 return c == '<' && '&lt;' ||
66031                  c == '>' && '&gt;' ||
66032                  c == '&' && '&amp;' ||
66033                  c == '"' && '&quot;' ||
66034                  '&#'+c.charCodeAt()+';'
66035         }
66036
66037
66038         copy$2(NodeType,Node);
66039         copy$2(NodeType,Node.prototype);
66040
66041         /**
66042          * @param callback return true for continue,false for break
66043          * @return boolean true: break visit;
66044          */
66045         function _visitNode(node,callback){
66046                 if(callback(node)){
66047                         return true;
66048                 }
66049                 if(node = node.firstChild){
66050                         do{
66051                                 if(_visitNode(node,callback)){return true}
66052                 }while(node=node.nextSibling)
66053             }
66054         }
66055
66056
66057
66058         function Document(){
66059         }
66060         function _onAddAttribute(doc,el,newAttr){
66061                 doc && doc._inc++;
66062                 var ns = newAttr.namespaceURI ;
66063                 if(ns == 'http://www.w3.org/2000/xmlns/'){
66064                         //update namespace
66065                         el._nsMap[newAttr.prefix?newAttr.localName:''] = newAttr.value;
66066                 }
66067         }
66068         function _onRemoveAttribute(doc,el,newAttr,remove){
66069                 doc && doc._inc++;
66070                 var ns = newAttr.namespaceURI ;
66071                 if(ns == 'http://www.w3.org/2000/xmlns/'){
66072                         //update namespace
66073                         delete el._nsMap[newAttr.prefix?newAttr.localName:''];
66074                 }
66075         }
66076         function _onUpdateChild(doc,el,newChild){
66077                 if(doc && doc._inc){
66078                         doc._inc++;
66079                         //update childNodes
66080                         var cs = el.childNodes;
66081                         if(newChild){
66082                                 cs[cs.length++] = newChild;
66083                         }else {
66084                                 //console.log(1)
66085                                 var child = el.firstChild;
66086                                 var i = 0;
66087                                 while(child){
66088                                         cs[i++] = child;
66089                                         child =child.nextSibling;
66090                                 }
66091                                 cs.length = i;
66092                         }
66093                 }
66094         }
66095
66096         /**
66097          * attributes;
66098          * children;
66099          * 
66100          * writeable properties:
66101          * nodeValue,Attr:value,CharacterData:data
66102          * prefix
66103          */
66104         function _removeChild(parentNode,child){
66105                 var previous = child.previousSibling;
66106                 var next = child.nextSibling;
66107                 if(previous){
66108                         previous.nextSibling = next;
66109                 }else {
66110                         parentNode.firstChild = next;
66111                 }
66112                 if(next){
66113                         next.previousSibling = previous;
66114                 }else {
66115                         parentNode.lastChild = previous;
66116                 }
66117                 _onUpdateChild(parentNode.ownerDocument,parentNode);
66118                 return child;
66119         }
66120         /**
66121          * preformance key(refChild == null)
66122          */
66123         function _insertBefore(parentNode,newChild,nextChild){
66124                 var cp = newChild.parentNode;
66125                 if(cp){
66126                         cp.removeChild(newChild);//remove and update
66127                 }
66128                 if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
66129                         var newFirst = newChild.firstChild;
66130                         if (newFirst == null) {
66131                                 return newChild;
66132                         }
66133                         var newLast = newChild.lastChild;
66134                 }else {
66135                         newFirst = newLast = newChild;
66136                 }
66137                 var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
66138
66139                 newFirst.previousSibling = pre;
66140                 newLast.nextSibling = nextChild;
66141                 
66142                 
66143                 if(pre){
66144                         pre.nextSibling = newFirst;
66145                 }else {
66146                         parentNode.firstChild = newFirst;
66147                 }
66148                 if(nextChild == null){
66149                         parentNode.lastChild = newLast;
66150                 }else {
66151                         nextChild.previousSibling = newLast;
66152                 }
66153                 do{
66154                         newFirst.parentNode = parentNode;
66155                 }while(newFirst !== newLast && (newFirst= newFirst.nextSibling))
66156                 _onUpdateChild(parentNode.ownerDocument||parentNode,parentNode);
66157                 //console.log(parentNode.lastChild.nextSibling == null)
66158                 if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
66159                         newChild.firstChild = newChild.lastChild = null;
66160                 }
66161                 return newChild;
66162         }
66163         function _appendSingleChild(parentNode,newChild){
66164                 var cp = newChild.parentNode;
66165                 if(cp){
66166                         var pre = parentNode.lastChild;
66167                         cp.removeChild(newChild);//remove and update
66168                         var pre = parentNode.lastChild;
66169                 }
66170                 var pre = parentNode.lastChild;
66171                 newChild.parentNode = parentNode;
66172                 newChild.previousSibling = pre;
66173                 newChild.nextSibling = null;
66174                 if(pre){
66175                         pre.nextSibling = newChild;
66176                 }else {
66177                         parentNode.firstChild = newChild;
66178                 }
66179                 parentNode.lastChild = newChild;
66180                 _onUpdateChild(parentNode.ownerDocument,parentNode,newChild);
66181                 return newChild;
66182                 //console.log("__aa",parentNode.lastChild.nextSibling == null)
66183         }
66184         Document.prototype = {
66185                 //implementation : null,
66186                 nodeName :  '#document',
66187                 nodeType :  DOCUMENT_NODE,
66188                 doctype :  null,
66189                 documentElement :  null,
66190                 _inc : 1,
66191                 
66192                 insertBefore :  function(newChild, refChild){//raises 
66193                         if(newChild.nodeType == DOCUMENT_FRAGMENT_NODE){
66194                                 var child = newChild.firstChild;
66195                                 while(child){
66196                                         var next = child.nextSibling;
66197                                         this.insertBefore(child,refChild);
66198                                         child = next;
66199                                 }
66200                                 return newChild;
66201                         }
66202                         if(this.documentElement == null && newChild.nodeType == ELEMENT_NODE){
66203                                 this.documentElement = newChild;
66204                         }
66205                         
66206                         return _insertBefore(this,newChild,refChild),(newChild.ownerDocument = this),newChild;
66207                 },
66208                 removeChild :  function(oldChild){
66209                         if(this.documentElement == oldChild){
66210                                 this.documentElement = null;
66211                         }
66212                         return _removeChild(this,oldChild);
66213                 },
66214                 // Introduced in DOM Level 2:
66215                 importNode : function(importedNode,deep){
66216                         return importNode(this,importedNode,deep);
66217                 },
66218                 // Introduced in DOM Level 2:
66219                 getElementById :        function(id){
66220                         var rtv = null;
66221                         _visitNode(this.documentElement,function(node){
66222                                 if(node.nodeType == ELEMENT_NODE){
66223                                         if(node.getAttribute('id') == id){
66224                                                 rtv = node;
66225                                                 return true;
66226                                         }
66227                                 }
66228                         });
66229                         return rtv;
66230                 },
66231                 
66232                 //document factory method:
66233                 createElement : function(tagName){
66234                         var node = new Element();
66235                         node.ownerDocument = this;
66236                         node.nodeName = tagName;
66237                         node.tagName = tagName;
66238                         node.childNodes = new NodeList();
66239                         var attrs       = node.attributes = new NamedNodeMap();
66240                         attrs._ownerElement = node;
66241                         return node;
66242                 },
66243                 createDocumentFragment :        function(){
66244                         var node = new DocumentFragment();
66245                         node.ownerDocument = this;
66246                         node.childNodes = new NodeList();
66247                         return node;
66248                 },
66249                 createTextNode :        function(data){
66250                         var node = new Text();
66251                         node.ownerDocument = this;
66252                         node.appendData(data);
66253                         return node;
66254                 },
66255                 createComment : function(data){
66256                         var node = new Comment();
66257                         node.ownerDocument = this;
66258                         node.appendData(data);
66259                         return node;
66260                 },
66261                 createCDATASection :    function(data){
66262                         var node = new CDATASection();
66263                         node.ownerDocument = this;
66264                         node.appendData(data);
66265                         return node;
66266                 },
66267                 createProcessingInstruction :   function(target,data){
66268                         var node = new ProcessingInstruction();
66269                         node.ownerDocument = this;
66270                         node.tagName = node.target = target;
66271                         node.nodeValue= node.data = data;
66272                         return node;
66273                 },
66274                 createAttribute :       function(name){
66275                         var node = new Attr();
66276                         node.ownerDocument      = this;
66277                         node.name = name;
66278                         node.nodeName   = name;
66279                         node.localName = name;
66280                         node.specified = true;
66281                         return node;
66282                 },
66283                 createEntityReference : function(name){
66284                         var node = new EntityReference();
66285                         node.ownerDocument      = this;
66286                         node.nodeName   = name;
66287                         return node;
66288                 },
66289                 // Introduced in DOM Level 2:
66290                 createElementNS :       function(namespaceURI,qualifiedName){
66291                         var node = new Element();
66292                         var pl = qualifiedName.split(':');
66293                         var attrs       = node.attributes = new NamedNodeMap();
66294                         node.childNodes = new NodeList();
66295                         node.ownerDocument = this;
66296                         node.nodeName = qualifiedName;
66297                         node.tagName = qualifiedName;
66298                         node.namespaceURI = namespaceURI;
66299                         if(pl.length == 2){
66300                                 node.prefix = pl[0];
66301                                 node.localName = pl[1];
66302                         }else {
66303                                 //el.prefix = null;
66304                                 node.localName = qualifiedName;
66305                         }
66306                         attrs._ownerElement = node;
66307                         return node;
66308                 },
66309                 // Introduced in DOM Level 2:
66310                 createAttributeNS :     function(namespaceURI,qualifiedName){
66311                         var node = new Attr();
66312                         var pl = qualifiedName.split(':');
66313                         node.ownerDocument = this;
66314                         node.nodeName = qualifiedName;
66315                         node.name = qualifiedName;
66316                         node.namespaceURI = namespaceURI;
66317                         node.specified = true;
66318                         if(pl.length == 2){
66319                                 node.prefix = pl[0];
66320                                 node.localName = pl[1];
66321                         }else {
66322                                 //el.prefix = null;
66323                                 node.localName = qualifiedName;
66324                         }
66325                         return node;
66326                 }
66327         };
66328         _extends(Document,Node);
66329
66330
66331         function Element() {
66332                 this._nsMap = {};
66333         }Element.prototype = {
66334                 nodeType : ELEMENT_NODE,
66335                 hasAttribute : function(name){
66336                         return this.getAttributeNode(name)!=null;
66337                 },
66338                 getAttribute : function(name){
66339                         var attr = this.getAttributeNode(name);
66340                         return attr && attr.value || '';
66341                 },
66342                 getAttributeNode : function(name){
66343                         return this.attributes.getNamedItem(name);
66344                 },
66345                 setAttribute : function(name, value){
66346                         var attr = this.ownerDocument.createAttribute(name);
66347                         attr.value = attr.nodeValue = "" + value;
66348                         this.setAttributeNode(attr);
66349                 },
66350                 removeAttribute : function(name){
66351                         var attr = this.getAttributeNode(name);
66352                         attr && this.removeAttributeNode(attr);
66353                 },
66354                 
66355                 //four real opeartion method
66356                 appendChild:function(newChild){
66357                         if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
66358                                 return this.insertBefore(newChild,null);
66359                         }else {
66360                                 return _appendSingleChild(this,newChild);
66361                         }
66362                 },
66363                 setAttributeNode : function(newAttr){
66364                         return this.attributes.setNamedItem(newAttr);
66365                 },
66366                 setAttributeNodeNS : function(newAttr){
66367                         return this.attributes.setNamedItemNS(newAttr);
66368                 },
66369                 removeAttributeNode : function(oldAttr){
66370                         //console.log(this == oldAttr.ownerElement)
66371                         return this.attributes.removeNamedItem(oldAttr.nodeName);
66372                 },
66373                 //get real attribute name,and remove it by removeAttributeNode
66374                 removeAttributeNS : function(namespaceURI, localName){
66375                         var old = this.getAttributeNodeNS(namespaceURI, localName);
66376                         old && this.removeAttributeNode(old);
66377                 },
66378                 
66379                 hasAttributeNS : function(namespaceURI, localName){
66380                         return this.getAttributeNodeNS(namespaceURI, localName)!=null;
66381                 },
66382                 getAttributeNS : function(namespaceURI, localName){
66383                         var attr = this.getAttributeNodeNS(namespaceURI, localName);
66384                         return attr && attr.value || '';
66385                 },
66386                 setAttributeNS : function(namespaceURI, qualifiedName, value){
66387                         var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
66388                         attr.value = attr.nodeValue = "" + value;
66389                         this.setAttributeNode(attr);
66390                 },
66391                 getAttributeNodeNS : function(namespaceURI, localName){
66392                         return this.attributes.getNamedItemNS(namespaceURI, localName);
66393                 },
66394                 
66395                 getElementsByTagName : function(tagName){
66396                         return new LiveNodeList(this,function(base){
66397                                 var ls = [];
66398                                 _visitNode(base,function(node){
66399                                         if(node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)){
66400                                                 ls.push(node);
66401                                         }
66402                                 });
66403                                 return ls;
66404                         });
66405                 },
66406                 getElementsByTagNameNS : function(namespaceURI, localName){
66407                         return new LiveNodeList(this,function(base){
66408                                 var ls = [];
66409                                 _visitNode(base,function(node){
66410                                         if(node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)){
66411                                                 ls.push(node);
66412                                         }
66413                                 });
66414                                 return ls;
66415                                 
66416                         });
66417                 }
66418         };
66419         Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
66420         Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
66421
66422
66423         _extends(Element,Node);
66424         function Attr() {
66425         }Attr.prototype.nodeType = ATTRIBUTE_NODE;
66426         _extends(Attr,Node);
66427
66428
66429         function CharacterData() {
66430         }CharacterData.prototype = {
66431                 data : '',
66432                 substringData : function(offset, count) {
66433                         return this.data.substring(offset, offset+count);
66434                 },
66435                 appendData: function(text) {
66436                         text = this.data+text;
66437                         this.nodeValue = this.data = text;
66438                         this.length = text.length;
66439                 },
66440                 insertData: function(offset,text) {
66441                         this.replaceData(offset,0,text);
66442                 
66443                 },
66444                 appendChild:function(newChild){
66445                         throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR])
66446                 },
66447                 deleteData: function(offset, count) {
66448                         this.replaceData(offset,count,"");
66449                 },
66450                 replaceData: function(offset, count, text) {
66451                         var start = this.data.substring(0,offset);
66452                         var end = this.data.substring(offset+count);
66453                         text = start + text + end;
66454                         this.nodeValue = this.data = text;
66455                         this.length = text.length;
66456                 }
66457         };
66458         _extends(CharacterData,Node);
66459         function Text() {
66460         }Text.prototype = {
66461                 nodeName : "#text",
66462                 nodeType : TEXT_NODE,
66463                 splitText : function(offset) {
66464                         var text = this.data;
66465                         var newText = text.substring(offset);
66466                         text = text.substring(0, offset);
66467                         this.data = this.nodeValue = text;
66468                         this.length = text.length;
66469                         var newNode = this.ownerDocument.createTextNode(newText);
66470                         if(this.parentNode){
66471                                 this.parentNode.insertBefore(newNode, this.nextSibling);
66472                         }
66473                         return newNode;
66474                 }
66475         };
66476         _extends(Text,CharacterData);
66477         function Comment() {
66478         }Comment.prototype = {
66479                 nodeName : "#comment",
66480                 nodeType : COMMENT_NODE
66481         };
66482         _extends(Comment,CharacterData);
66483
66484         function CDATASection() {
66485         }CDATASection.prototype = {
66486                 nodeName : "#cdata-section",
66487                 nodeType : CDATA_SECTION_NODE
66488         };
66489         _extends(CDATASection,CharacterData);
66490
66491
66492         function DocumentType() {
66493         }DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
66494         _extends(DocumentType,Node);
66495
66496         function Notation() {
66497         }Notation.prototype.nodeType = NOTATION_NODE;
66498         _extends(Notation,Node);
66499
66500         function Entity() {
66501         }Entity.prototype.nodeType = ENTITY_NODE;
66502         _extends(Entity,Node);
66503
66504         function EntityReference() {
66505         }EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
66506         _extends(EntityReference,Node);
66507
66508         function DocumentFragment() {
66509         }DocumentFragment.prototype.nodeName =  "#document-fragment";
66510         DocumentFragment.prototype.nodeType =   DOCUMENT_FRAGMENT_NODE;
66511         _extends(DocumentFragment,Node);
66512
66513
66514         function ProcessingInstruction() {
66515         }
66516         ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
66517         _extends(ProcessingInstruction,Node);
66518         function XMLSerializer$1(){}
66519         XMLSerializer$1.prototype.serializeToString = function(node,isHtml,nodeFilter){
66520                 return nodeSerializeToString.call(node,isHtml,nodeFilter);
66521         };
66522         Node.prototype.toString = nodeSerializeToString;
66523         function nodeSerializeToString(isHtml,nodeFilter){
66524                 var buf = [];
66525                 var refNode = this.nodeType == 9?this.documentElement:this;
66526                 var prefix = refNode.prefix;
66527                 var uri = refNode.namespaceURI;
66528                 
66529                 if(uri && prefix == null){
66530                         //console.log(prefix)
66531                         var prefix = refNode.lookupPrefix(uri);
66532                         if(prefix == null){
66533                                 //isHTML = true;
66534                                 var visibleNamespaces=[
66535                                 {namespace:uri,prefix:null}
66536                                 //{namespace:uri,prefix:''}
66537                                 ];
66538                         }
66539                 }
66540                 serializeToString(this,buf,isHtml,nodeFilter,visibleNamespaces);
66541                 //console.log('###',this.nodeType,uri,prefix,buf.join(''))
66542                 return buf.join('');
66543         }
66544         function needNamespaceDefine(node,isHTML, visibleNamespaces) {
66545                 var prefix = node.prefix||'';
66546                 var uri = node.namespaceURI;
66547                 if (!prefix && !uri){
66548                         return false;
66549                 }
66550                 if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" 
66551                         || uri == 'http://www.w3.org/2000/xmlns/'){
66552                         return false;
66553                 }
66554                 
66555                 var i = visibleNamespaces.length; 
66556                 //console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces)
66557                 while (i--) {
66558                         var ns = visibleNamespaces[i];
66559                         // get namespace prefix
66560                         //console.log(node.nodeType,node.tagName,ns.prefix,prefix)
66561                         if (ns.prefix == prefix){
66562                                 return ns.namespace != uri;
66563                         }
66564                 }
66565                 //console.log(isHTML,uri,prefix=='')
66566                 //if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){
66567                 //      return false;
66568                 //}
66569                 //node.flag = '11111'
66570                 //console.error(3,true,node.flag,node.prefix,node.namespaceURI)
66571                 return true;
66572         }
66573         function serializeToString(node,buf,isHTML,nodeFilter,visibleNamespaces){
66574                 if(nodeFilter){
66575                         node = nodeFilter(node);
66576                         if(node){
66577                                 if(typeof node == 'string'){
66578                                         buf.push(node);
66579                                         return;
66580                                 }
66581                         }else {
66582                                 return;
66583                         }
66584                         //buf.sort.apply(attrs, attributeSorter);
66585                 }
66586                 switch(node.nodeType){
66587                 case ELEMENT_NODE:
66588                         if (!visibleNamespaces) visibleNamespaces = [];
66589                         var startVisibleNamespaces = visibleNamespaces.length;
66590                         var attrs = node.attributes;
66591                         var len = attrs.length;
66592                         var child = node.firstChild;
66593                         var nodeName = node.tagName;
66594                         
66595                         isHTML =  (htmlns === node.namespaceURI) ||isHTML; 
66596                         buf.push('<',nodeName);
66597                         
66598                         
66599                         
66600                         for(var i=0;i<len;i++){
66601                                 // add namespaces for attributes
66602                                 var attr = attrs.item(i);
66603                                 if (attr.prefix == 'xmlns') {
66604                                         visibleNamespaces.push({ prefix: attr.localName, namespace: attr.value });
66605                                 }else if(attr.nodeName == 'xmlns'){
66606                                         visibleNamespaces.push({ prefix: '', namespace: attr.value });
66607                                 }
66608                         }
66609                         for(var i=0;i<len;i++){
66610                                 var attr = attrs.item(i);
66611                                 if (needNamespaceDefine(attr,isHTML, visibleNamespaces)) {
66612                                         var prefix = attr.prefix||'';
66613                                         var uri = attr.namespaceURI;
66614                                         var ns = prefix ? ' xmlns:' + prefix : " xmlns";
66615                                         buf.push(ns, '="' , uri , '"');
66616                                         visibleNamespaces.push({ prefix: prefix, namespace:uri });
66617                                 }
66618                                 serializeToString(attr,buf,isHTML,nodeFilter,visibleNamespaces);
66619                         }
66620                         // add namespace for current node               
66621                         if (needNamespaceDefine(node,isHTML, visibleNamespaces)) {
66622                                 var prefix = node.prefix||'';
66623                                 var uri = node.namespaceURI;
66624                                 var ns = prefix ? ' xmlns:' + prefix : " xmlns";
66625                                 buf.push(ns, '="' , uri , '"');
66626                                 visibleNamespaces.push({ prefix: prefix, namespace:uri });
66627                         }
66628                         
66629                         if(child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)){
66630                                 buf.push('>');
66631                                 //if is cdata child node
66632                                 if(isHTML && /^script$/i.test(nodeName)){
66633                                         while(child){
66634                                                 if(child.data){
66635                                                         buf.push(child.data);
66636                                                 }else {
66637                                                         serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66638                                                 }
66639                                                 child = child.nextSibling;
66640                                         }
66641                                 }else
66642                                 {
66643                                         while(child){
66644                                                 serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66645                                                 child = child.nextSibling;
66646                                         }
66647                                 }
66648                                 buf.push('</',nodeName,'>');
66649                         }else {
66650                                 buf.push('/>');
66651                         }
66652                         // remove added visible namespaces
66653                         //visibleNamespaces.length = startVisibleNamespaces;
66654                         return;
66655                 case DOCUMENT_NODE:
66656                 case DOCUMENT_FRAGMENT_NODE:
66657                         var child = node.firstChild;
66658                         while(child){
66659                                 serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66660                                 child = child.nextSibling;
66661                         }
66662                         return;
66663                 case ATTRIBUTE_NODE:
66664                         return buf.push(' ',node.name,'="',node.value.replace(/[<&"]/g,_xmlEncoder),'"');
66665                 case TEXT_NODE:
66666                         return buf.push(node.data.replace(/[<&]/g,_xmlEncoder));
66667                 case CDATA_SECTION_NODE:
66668                         return buf.push( '<![CDATA[',node.data,']]>');
66669                 case COMMENT_NODE:
66670                         return buf.push( "<!--",node.data,"-->");
66671                 case DOCUMENT_TYPE_NODE:
66672                         var pubid = node.publicId;
66673                         var sysid = node.systemId;
66674                         buf.push('<!DOCTYPE ',node.name);
66675                         if(pubid){
66676                                 buf.push(' PUBLIC "',pubid);
66677                                 if (sysid && sysid!='.') {
66678                                         buf.push( '" "',sysid);
66679                                 }
66680                                 buf.push('">');
66681                         }else if(sysid && sysid!='.'){
66682                                 buf.push(' SYSTEM "',sysid,'">');
66683                         }else {
66684                                 var sub = node.internalSubset;
66685                                 if(sub){
66686                                         buf.push(" [",sub,"]");
66687                                 }
66688                                 buf.push(">");
66689                         }
66690                         return;
66691                 case PROCESSING_INSTRUCTION_NODE:
66692                         return buf.push( "<?",node.target," ",node.data,"?>");
66693                 case ENTITY_REFERENCE_NODE:
66694                         return buf.push( '&',node.nodeName,';');
66695                 //case ENTITY_NODE:
66696                 //case NOTATION_NODE:
66697                 default:
66698                         buf.push('??',node.nodeName);
66699                 }
66700         }
66701         function importNode(doc,node,deep){
66702                 var node2;
66703                 switch (node.nodeType) {
66704                 case ELEMENT_NODE:
66705                         node2 = node.cloneNode(false);
66706                         node2.ownerDocument = doc;
66707                         //var attrs = node2.attributes;
66708                         //var len = attrs.length;
66709                         //for(var i=0;i<len;i++){
66710                                 //node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
66711                         //}
66712                 case DOCUMENT_FRAGMENT_NODE:
66713                         break;
66714                 case ATTRIBUTE_NODE:
66715                         deep = true;
66716                         break;
66717                 //case ENTITY_REFERENCE_NODE:
66718                 //case PROCESSING_INSTRUCTION_NODE:
66719                 ////case TEXT_NODE:
66720                 //case CDATA_SECTION_NODE:
66721                 //case COMMENT_NODE:
66722                 //      deep = false;
66723                 //      break;
66724                 //case DOCUMENT_NODE:
66725                 //case DOCUMENT_TYPE_NODE:
66726                 //cannot be imported.
66727                 //case ENTITY_NODE:
66728                 //case NOTATION_NODE:
66729                 //can not hit in level3
66730                 //default:throw e;
66731                 }
66732                 if(!node2){
66733                         node2 = node.cloneNode(false);//false
66734                 }
66735                 node2.ownerDocument = doc;
66736                 node2.parentNode = null;
66737                 if(deep){
66738                         var child = node.firstChild;
66739                         while(child){
66740                                 node2.appendChild(importNode(doc,child,deep));
66741                                 child = child.nextSibling;
66742                         }
66743                 }
66744                 return node2;
66745         }
66746         //
66747         //var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1,
66748         //                                      attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,};
66749         function cloneNode(doc,node,deep){
66750                 var node2 = new node.constructor();
66751                 for(var n in node){
66752                         var v = node[n];
66753                         if(typeof v != 'object' ){
66754                                 if(v != node2[n]){
66755                                         node2[n] = v;
66756                                 }
66757                         }
66758                 }
66759                 if(node.childNodes){
66760                         node2.childNodes = new NodeList();
66761                 }
66762                 node2.ownerDocument = doc;
66763                 switch (node2.nodeType) {
66764                 case ELEMENT_NODE:
66765                         var attrs       = node.attributes;
66766                         var attrs2      = node2.attributes = new NamedNodeMap();
66767                         var len = attrs.length;
66768                         attrs2._ownerElement = node2;
66769                         for(var i=0;i<len;i++){
66770                                 node2.setAttributeNode(cloneNode(doc,attrs.item(i),true));
66771                         }
66772                         break;  case ATTRIBUTE_NODE:
66773                         deep = true;
66774                 }
66775                 if(deep){
66776                         var child = node.firstChild;
66777                         while(child){
66778                                 node2.appendChild(cloneNode(doc,child,deep));
66779                                 child = child.nextSibling;
66780                         }
66781                 }
66782                 return node2;
66783         }
66784
66785         function __set__(object,key,value){
66786                 object[key] = value;
66787         }
66788         //do dynamic
66789         try{
66790                 if(Object.defineProperty){
66791                         Object.defineProperty(LiveNodeList.prototype,'length',{
66792                                 get:function(){
66793                                         _updateLiveList(this);
66794                                         return this.$$length;
66795                                 }
66796                         });
66797                         Object.defineProperty(Node.prototype,'textContent',{
66798                                 get:function(){
66799                                         return getTextContent(this);
66800                                 },
66801                                 set:function(data){
66802                                         switch(this.nodeType){
66803                                         case ELEMENT_NODE:
66804                                         case DOCUMENT_FRAGMENT_NODE:
66805                                                 while(this.firstChild){
66806                                                         this.removeChild(this.firstChild);
66807                                                 }
66808                                                 if(data || String(data)){
66809                                                         this.appendChild(this.ownerDocument.createTextNode(data));
66810                                                 }
66811                                                 break;
66812                                         default:
66813                                                 //TODO:
66814                                                 this.data = data;
66815                                                 this.value = data;
66816                                                 this.nodeValue = data;
66817                                         }
66818                                 }
66819                         });
66820                         
66821                         function getTextContent(node){
66822                                 switch(node.nodeType){
66823                                 case ELEMENT_NODE:
66824                                 case DOCUMENT_FRAGMENT_NODE:
66825                                         var buf = [];
66826                                         node = node.firstChild;
66827                                         while(node){
66828                                                 if(node.nodeType!==7 && node.nodeType !==8){
66829                                                         buf.push(getTextContent(node));
66830                                                 }
66831                                                 node = node.nextSibling;
66832                                         }
66833                                         return buf.join('');
66834                                 default:
66835                                         return node.nodeValue;
66836                                 }
66837                         }
66838                         __set__ = function(object,key,value){
66839                                 //console.log(value)
66840                                 object['$$'+key] = value;
66841                         };
66842                 }
66843         }catch(e){//ie8
66844         }
66845
66846         //if(typeof require == 'function'){
66847                 var DOMImplementation_1 = DOMImplementation;
66848                 var XMLSerializer_1 = XMLSerializer$1;
66849         //}
66850
66851         var dom = {
66852                 DOMImplementation: DOMImplementation_1,
66853                 XMLSerializer: XMLSerializer_1
66854         };
66855
66856         var domParser = createCommonjsModule(function (module, exports) {
66857         function DOMParser(options){
66858                 this.options = options ||{locator:{}};
66859                 
66860         }
66861         DOMParser.prototype.parseFromString = function(source,mimeType){
66862                 var options = this.options;
66863                 var sax =  new XMLReader();
66864                 var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler
66865                 var errorHandler = options.errorHandler;
66866                 var locator = options.locator;
66867                 var defaultNSMap = options.xmlns||{};
66868                 var entityMap = {'lt':'<','gt':'>','amp':'&','quot':'"','apos':"'"};
66869                 if(locator){
66870                         domBuilder.setDocumentLocator(locator);
66871                 }
66872                 
66873                 sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator);
66874                 sax.domBuilder = options.domBuilder || domBuilder;
66875                 if(/\/x?html?$/.test(mimeType)){
66876                         entityMap.nbsp = '\xa0';
66877                         entityMap.copy = '\xa9';
66878                         defaultNSMap['']= 'http://www.w3.org/1999/xhtml';
66879                 }
66880                 defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
66881                 if(source){
66882                         sax.parse(source,defaultNSMap,entityMap);
66883                 }else {
66884                         sax.errorHandler.error("invalid doc source");
66885                 }
66886                 return domBuilder.doc;
66887         };
66888         function buildErrorHandler(errorImpl,domBuilder,locator){
66889                 if(!errorImpl){
66890                         if(domBuilder instanceof DOMHandler){
66891                                 return domBuilder;
66892                         }
66893                         errorImpl = domBuilder ;
66894                 }
66895                 var errorHandler = {};
66896                 var isCallback = errorImpl instanceof Function;
66897                 locator = locator||{};
66898                 function build(key){
66899                         var fn = errorImpl[key];
66900                         if(!fn && isCallback){
66901                                 fn = errorImpl.length == 2?function(msg){errorImpl(key,msg);}:errorImpl;
66902                         }
66903                         errorHandler[key] = fn && function(msg){
66904                                 fn('[xmldom '+key+']\t'+msg+_locator(locator));
66905                         }||function(){};
66906                 }
66907                 build('warning');
66908                 build('error');
66909                 build('fatalError');
66910                 return errorHandler;
66911         }
66912
66913         //console.log('#\n\n\n\n\n\n\n####')
66914         /**
66915          * +ContentHandler+ErrorHandler
66916          * +LexicalHandler+EntityResolver2
66917          * -DeclHandler-DTDHandler 
66918          * 
66919          * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
66920          * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
66921          * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
66922          */
66923         function DOMHandler() {
66924             this.cdata = false;
66925         }
66926         function position(locator,node){
66927                 node.lineNumber = locator.lineNumber;
66928                 node.columnNumber = locator.columnNumber;
66929         }
66930         /**
66931          * @see org.xml.sax.ContentHandler#startDocument
66932          * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
66933          */ 
66934         DOMHandler.prototype = {
66935                 startDocument : function() {
66936                 this.doc = new DOMImplementation().createDocument(null, null, null);
66937                 if (this.locator) {
66938                         this.doc.documentURI = this.locator.systemId;
66939                 }
66940                 },
66941                 startElement:function(namespaceURI, localName, qName, attrs) {
66942                         var doc = this.doc;
66943                     var el = doc.createElementNS(namespaceURI, qName||localName);
66944                     var len = attrs.length;
66945                     appendElement(this, el);
66946                     this.currentElement = el;
66947                     
66948                         this.locator && position(this.locator,el);
66949                     for (var i = 0 ; i < len; i++) {
66950                         var namespaceURI = attrs.getURI(i);
66951                         var value = attrs.getValue(i);
66952                         var qName = attrs.getQName(i);
66953                                 var attr = doc.createAttributeNS(namespaceURI, qName);
66954                                 this.locator &&position(attrs.getLocator(i),attr);
66955                                 attr.value = attr.nodeValue = value;
66956                                 el.setAttributeNode(attr);
66957                     }
66958                 },
66959                 endElement:function(namespaceURI, localName, qName) {
66960                         var current = this.currentElement;
66961                         var tagName = current.tagName;
66962                         this.currentElement = current.parentNode;
66963                 },
66964                 startPrefixMapping:function(prefix, uri) {
66965                 },
66966                 endPrefixMapping:function(prefix) {
66967                 },
66968                 processingInstruction:function(target, data) {
66969                     var ins = this.doc.createProcessingInstruction(target, data);
66970                     this.locator && position(this.locator,ins);
66971                     appendElement(this, ins);
66972                 },
66973                 ignorableWhitespace:function(ch, start, length) {
66974                 },
66975                 characters:function(chars, start, length) {
66976                         chars = _toString.apply(this,arguments);
66977                         //console.log(chars)
66978                         if(chars){
66979                                 if (this.cdata) {
66980                                         var charNode = this.doc.createCDATASection(chars);
66981                                 } else {
66982                                         var charNode = this.doc.createTextNode(chars);
66983                                 }
66984                                 if(this.currentElement){
66985                                         this.currentElement.appendChild(charNode);
66986                                 }else if(/^\s*$/.test(chars)){
66987                                         this.doc.appendChild(charNode);
66988                                         //process xml
66989                                 }
66990                                 this.locator && position(this.locator,charNode);
66991                         }
66992                 },
66993                 skippedEntity:function(name) {
66994                 },
66995                 endDocument:function() {
66996                         this.doc.normalize();
66997                 },
66998                 setDocumentLocator:function (locator) {
66999                     if(this.locator = locator){// && !('lineNumber' in locator)){
67000                         locator.lineNumber = 0;
67001                     }
67002                 },
67003                 //LexicalHandler
67004                 comment:function(chars, start, length) {
67005                         chars = _toString.apply(this,arguments);
67006                     var comm = this.doc.createComment(chars);
67007                     this.locator && position(this.locator,comm);
67008                     appendElement(this, comm);
67009                 },
67010                 
67011                 startCDATA:function() {
67012                     //used in characters() methods
67013                     this.cdata = true;
67014                 },
67015                 endCDATA:function() {
67016                     this.cdata = false;
67017                 },
67018                 
67019                 startDTD:function(name, publicId, systemId) {
67020                         var impl = this.doc.implementation;
67021                     if (impl && impl.createDocumentType) {
67022                         var dt = impl.createDocumentType(name, publicId, systemId);
67023                         this.locator && position(this.locator,dt);
67024                         appendElement(this, dt);
67025                     }
67026                 },
67027                 /**
67028                  * @see org.xml.sax.ErrorHandler
67029                  * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
67030                  */
67031                 warning:function(error) {
67032                         console.warn('[xmldom warning]\t'+error,_locator(this.locator));
67033                 },
67034                 error:function(error) {
67035                         console.error('[xmldom error]\t'+error,_locator(this.locator));
67036                 },
67037                 fatalError:function(error) {
67038                         console.error('[xmldom fatalError]\t'+error,_locator(this.locator));
67039                     throw error;
67040                 }
67041         };
67042         function _locator(l){
67043                 if(l){
67044                         return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']'
67045                 }
67046         }
67047         function _toString(chars,start,length){
67048                 if(typeof chars == 'string'){
67049                         return chars.substr(start,length)
67050                 }else {//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
67051                         if(chars.length >= start+length || start){
67052                                 return new java.lang.String(chars,start,length)+'';
67053                         }
67054                         return chars;
67055                 }
67056         }
67057
67058         /*
67059          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
67060          * used method of org.xml.sax.ext.LexicalHandler:
67061          *  #comment(chars, start, length)
67062          *  #startCDATA()
67063          *  #endCDATA()
67064          *  #startDTD(name, publicId, systemId)
67065          *
67066          *
67067          * IGNORED method of org.xml.sax.ext.LexicalHandler:
67068          *  #endDTD()
67069          *  #startEntity(name)
67070          *  #endEntity(name)
67071          *
67072          *
67073          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
67074          * IGNORED method of org.xml.sax.ext.DeclHandler
67075          *      #attributeDecl(eName, aName, type, mode, value)
67076          *  #elementDecl(name, model)
67077          *  #externalEntityDecl(name, publicId, systemId)
67078          *  #internalEntityDecl(name, value)
67079          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
67080          * IGNORED method of org.xml.sax.EntityResolver2
67081          *  #resolveEntity(String name,String publicId,String baseURI,String systemId)
67082          *  #resolveEntity(publicId, systemId)
67083          *  #getExternalSubset(name, baseURI)
67084          * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
67085          * IGNORED method of org.xml.sax.DTDHandler
67086          *  #notationDecl(name, publicId, systemId) {};
67087          *  #unparsedEntityDecl(name, publicId, systemId, notationName) {};
67088          */
67089         "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){
67090                 DOMHandler.prototype[key] = function(){return null};
67091         });
67092
67093         /* 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 */
67094         function appendElement (hander,node) {
67095             if (!hander.currentElement) {
67096                 hander.doc.appendChild(node);
67097             } else {
67098                 hander.currentElement.appendChild(node);
67099             }
67100         }//appendChild and setAttributeNS are preformance key
67101
67102         //if(typeof require == 'function'){
67103                 var XMLReader = sax.XMLReader;
67104                 var DOMImplementation = exports.DOMImplementation = dom.DOMImplementation;
67105                 exports.XMLSerializer = dom.XMLSerializer ;
67106                 exports.DOMParser = DOMParser;
67107         //}
67108         });
67109
67110         var togeojson = createCommonjsModule(function (module, exports) {
67111         var toGeoJSON = (function() {
67112
67113             var removeSpace = /\s*/g,
67114                 trimSpace = /^\s*|\s*$/g,
67115                 splitSpace = /\s+/;
67116             // generate a short, numeric hash of a string
67117             function okhash(x) {
67118                 if (!x || !x.length) return 0;
67119                 for (var i = 0, h = 0; i < x.length; i++) {
67120                     h = ((h << 5) - h) + x.charCodeAt(i) | 0;
67121                 } return h;
67122             }
67123             // all Y children of X
67124             function get(x, y) { return x.getElementsByTagName(y); }
67125             function attr(x, y) { return x.getAttribute(y); }
67126             function attrf(x, y) { return parseFloat(attr(x, y)); }
67127             // one Y child of X, if any, otherwise null
67128             function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
67129             // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
67130             function norm(el) { if (el.normalize) { el.normalize(); } return el; }
67131             // cast array x into numbers
67132             function numarray(x) {
67133                 for (var j = 0, o = []; j < x.length; j++) { o[j] = parseFloat(x[j]); }
67134                 return o;
67135             }
67136             // get the content of a text node, if any
67137             function nodeVal(x) {
67138                 if (x) { norm(x); }
67139                 return (x && x.textContent) || '';
67140             }
67141             // get the contents of multiple text nodes, if present
67142             function getMulti(x, ys) {
67143                 var o = {}, n, k;
67144                 for (k = 0; k < ys.length; k++) {
67145                     n = get1(x, ys[k]);
67146                     if (n) o[ys[k]] = nodeVal(n);
67147                 }
67148                 return o;
67149             }
67150             // add properties of Y to X, overwriting if present in both
67151             function extend(x, y) { for (var k in y) x[k] = y[k]; }
67152             // get one coordinate from a coordinate array, if any
67153             function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
67154             // get all coordinates from a coordinate array as [[],[]]
67155             function coord(v) {
67156                 var coords = v.replace(trimSpace, '').split(splitSpace),
67157                     o = [];
67158                 for (var i = 0; i < coords.length; i++) {
67159                     o.push(coord1(coords[i]));
67160                 }
67161                 return o;
67162             }
67163             function coordPair(x) {
67164                 var ll = [attrf(x, 'lon'), attrf(x, 'lat')],
67165                     ele = get1(x, 'ele'),
67166                     // handle namespaced attribute in browser
67167                     heartRate = get1(x, 'gpxtpx:hr') || get1(x, 'hr'),
67168                     time = get1(x, 'time'),
67169                     e;
67170                 if (ele) {
67171                     e = parseFloat(nodeVal(ele));
67172                     if (!isNaN(e)) {
67173                         ll.push(e);
67174                     }
67175                 }
67176                 return {
67177                     coordinates: ll,
67178                     time: time ? nodeVal(time) : null,
67179                     heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null
67180                 };
67181             }
67182
67183             // create a new feature collection parent object
67184             function fc() {
67185                 return {
67186                     type: 'FeatureCollection',
67187                     features: []
67188                 };
67189             }
67190
67191             var serializer;
67192             if (typeof XMLSerializer !== 'undefined') {
67193                 /* istanbul ignore next */
67194                 serializer = new XMLSerializer();
67195             // only require xmldom in a node environment
67196             } else if ( typeof process === 'object' && !process.browser) {
67197                 serializer = new (domParser.XMLSerializer)();
67198             }
67199             function xml2str(str) {
67200                 // IE9 will create a new XMLSerializer but it'll crash immediately.
67201                 // This line is ignored because we don't run coverage tests in IE9
67202                 /* istanbul ignore next */
67203                 if (str.xml !== undefined) return str.xml;
67204                 return serializer.serializeToString(str);
67205             }
67206
67207             var t = {
67208                 kml: function(doc) {
67209
67210                     var gj = fc(),
67211                         // styleindex keeps track of hashed styles in order to match features
67212                         styleIndex = {}, styleByHash = {},
67213                         // stylemapindex keeps track of style maps to expose in properties
67214                         styleMapIndex = {},
67215                         // atomic geospatial types supported by KML - MultiGeometry is
67216                         // handled separately
67217                         geotypes = ['Polygon', 'LineString', 'Point', 'Track', 'gx:Track'],
67218                         // all root placemarks in the file
67219                         placemarks = get(doc, 'Placemark'),
67220                         styles = get(doc, 'Style'),
67221                         styleMaps = get(doc, 'StyleMap');
67222
67223                     for (var k = 0; k < styles.length; k++) {
67224                         var hash = okhash(xml2str(styles[k])).toString(16);
67225                         styleIndex['#' + attr(styles[k], 'id')] = hash;
67226                         styleByHash[hash] = styles[k];
67227                     }
67228                     for (var l = 0; l < styleMaps.length; l++) {
67229                         styleIndex['#' + attr(styleMaps[l], 'id')] = okhash(xml2str(styleMaps[l])).toString(16);
67230                         var pairs = get(styleMaps[l], 'Pair');
67231                         var pairsMap = {};
67232                         for (var m = 0; m < pairs.length; m++) {
67233                             pairsMap[nodeVal(get1(pairs[m], 'key'))] = nodeVal(get1(pairs[m], 'styleUrl'));
67234                         }
67235                         styleMapIndex['#' + attr(styleMaps[l], 'id')] = pairsMap;
67236
67237                     }
67238                     for (var j = 0; j < placemarks.length; j++) {
67239                         gj.features = gj.features.concat(getPlacemark(placemarks[j]));
67240                     }
67241                     function kmlColor(v) {
67242                         var color, opacity;
67243                         v = v || '';
67244                         if (v.substr(0, 1) === '#') { v = v.substr(1); }
67245                         if (v.length === 6 || v.length === 3) { color = v; }
67246                         if (v.length === 8) {
67247                             opacity = parseInt(v.substr(0, 2), 16) / 255;
67248                             color = '#' + v.substr(6, 2) +
67249                                 v.substr(4, 2) +
67250                                 v.substr(2, 2);
67251                         }
67252                         return [color, isNaN(opacity) ? undefined : opacity];
67253                     }
67254                     function gxCoord(v) { return numarray(v.split(' ')); }
67255                     function gxCoords(root) {
67256                         var elems = get(root, 'coord'), coords = [], times = [];
67257                         if (elems.length === 0) elems = get(root, 'gx:coord');
67258                         for (var i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elems[i])));
67259                         var timeElems = get(root, 'when');
67260                         for (var j = 0; j < timeElems.length; j++) times.push(nodeVal(timeElems[j]));
67261                         return {
67262                             coords: coords,
67263                             times: times
67264                         };
67265                     }
67266                     function getGeometry(root) {
67267                         var geomNode, geomNodes, i, j, k, geoms = [], coordTimes = [];
67268                         if (get1(root, 'MultiGeometry')) { return getGeometry(get1(root, 'MultiGeometry')); }
67269                         if (get1(root, 'MultiTrack')) { return getGeometry(get1(root, 'MultiTrack')); }
67270                         if (get1(root, 'gx:MultiTrack')) { return getGeometry(get1(root, 'gx:MultiTrack')); }
67271                         for (i = 0; i < geotypes.length; i++) {
67272                             geomNodes = get(root, geotypes[i]);
67273                             if (geomNodes) {
67274                                 for (j = 0; j < geomNodes.length; j++) {
67275                                     geomNode = geomNodes[j];
67276                                     if (geotypes[i] === 'Point') {
67277                                         geoms.push({
67278                                             type: 'Point',
67279                                             coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
67280                                         });
67281                                     } else if (geotypes[i] === 'LineString') {
67282                                         geoms.push({
67283                                             type: 'LineString',
67284                                             coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
67285                                         });
67286                                     } else if (geotypes[i] === 'Polygon') {
67287                                         var rings = get(geomNode, 'LinearRing'),
67288                                             coords = [];
67289                                         for (k = 0; k < rings.length; k++) {
67290                                             coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
67291                                         }
67292                                         geoms.push({
67293                                             type: 'Polygon',
67294                                             coordinates: coords
67295                                         });
67296                                     } else if (geotypes[i] === 'Track' ||
67297                                         geotypes[i] === 'gx:Track') {
67298                                         var track = gxCoords(geomNode);
67299                                         geoms.push({
67300                                             type: 'LineString',
67301                                             coordinates: track.coords
67302                                         });
67303                                         if (track.times.length) coordTimes.push(track.times);
67304                                     }
67305                                 }
67306                             }
67307                         }
67308                         return {
67309                             geoms: geoms,
67310                             coordTimes: coordTimes
67311                         };
67312                     }
67313                     function getPlacemark(root) {
67314                         var geomsAndTimes = getGeometry(root), i, properties = {},
67315                             name = nodeVal(get1(root, 'name')),
67316                             address = nodeVal(get1(root, 'address')),
67317                             styleUrl = nodeVal(get1(root, 'styleUrl')),
67318                             description = nodeVal(get1(root, 'description')),
67319                             timeSpan = get1(root, 'TimeSpan'),
67320                             timeStamp = get1(root, 'TimeStamp'),
67321                             extendedData = get1(root, 'ExtendedData'),
67322                             lineStyle = get1(root, 'LineStyle'),
67323                             polyStyle = get1(root, 'PolyStyle'),
67324                             visibility = get1(root, 'visibility');
67325
67326                         if (!geomsAndTimes.geoms.length) return [];
67327                         if (name) properties.name = name;
67328                         if (address) properties.address = address;
67329                         if (styleUrl) {
67330                             if (styleUrl[0] !== '#') {
67331                                 styleUrl = '#' + styleUrl;
67332                             }
67333
67334                             properties.styleUrl = styleUrl;
67335                             if (styleIndex[styleUrl]) {
67336                                 properties.styleHash = styleIndex[styleUrl];
67337                             }
67338                             if (styleMapIndex[styleUrl]) {
67339                                 properties.styleMapHash = styleMapIndex[styleUrl];
67340                                 properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal];
67341                             }
67342                             // Try to populate the lineStyle or polyStyle since we got the style hash
67343                             var style = styleByHash[properties.styleHash];
67344                             if (style) {
67345                                 if (!lineStyle) lineStyle = get1(style, 'LineStyle');
67346                                 if (!polyStyle) polyStyle = get1(style, 'PolyStyle');
67347                             }
67348                         }
67349                         if (description) properties.description = description;
67350                         if (timeSpan) {
67351                             var begin = nodeVal(get1(timeSpan, 'begin'));
67352                             var end = nodeVal(get1(timeSpan, 'end'));
67353                             properties.timespan = { begin: begin, end: end };
67354                         }
67355                         if (timeStamp) {
67356                             properties.timestamp = nodeVal(get1(timeStamp, 'when'));
67357                         }
67358                         if (lineStyle) {
67359                             var linestyles = kmlColor(nodeVal(get1(lineStyle, 'color'))),
67360                                 color = linestyles[0],
67361                                 opacity = linestyles[1],
67362                                 width = parseFloat(nodeVal(get1(lineStyle, 'width')));
67363                             if (color) properties.stroke = color;
67364                             if (!isNaN(opacity)) properties['stroke-opacity'] = opacity;
67365                             if (!isNaN(width)) properties['stroke-width'] = width;
67366                         }
67367                         if (polyStyle) {
67368                             var polystyles = kmlColor(nodeVal(get1(polyStyle, 'color'))),
67369                                 pcolor = polystyles[0],
67370                                 popacity = polystyles[1],
67371                                 fill = nodeVal(get1(polyStyle, 'fill')),
67372                                 outline = nodeVal(get1(polyStyle, 'outline'));
67373                             if (pcolor) properties.fill = pcolor;
67374                             if (!isNaN(popacity)) properties['fill-opacity'] = popacity;
67375                             if (fill) properties['fill-opacity'] = fill === '1' ? properties['fill-opacity'] || 1 : 0;
67376                             if (outline) properties['stroke-opacity'] = outline === '1' ? properties['stroke-opacity'] || 1 : 0;
67377                         }
67378                         if (extendedData) {
67379                             var datas = get(extendedData, 'Data'),
67380                                 simpleDatas = get(extendedData, 'SimpleData');
67381
67382                             for (i = 0; i < datas.length; i++) {
67383                                 properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
67384                             }
67385                             for (i = 0; i < simpleDatas.length; i++) {
67386                                 properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
67387                             }
67388                         }
67389                         if (visibility) {
67390                             properties.visibility = nodeVal(visibility);
67391                         }
67392                         if (geomsAndTimes.coordTimes.length) {
67393                             properties.coordTimes = (geomsAndTimes.coordTimes.length === 1) ?
67394                                 geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes;
67395                         }
67396                         var feature = {
67397                             type: 'Feature',
67398                             geometry: (geomsAndTimes.geoms.length === 1) ? geomsAndTimes.geoms[0] : {
67399                                 type: 'GeometryCollection',
67400                                 geometries: geomsAndTimes.geoms
67401                             },
67402                             properties: properties
67403                         };
67404                         if (attr(root, 'id')) feature.id = attr(root, 'id');
67405                         return [feature];
67406                     }
67407                     return gj;
67408                 },
67409                 gpx: function(doc) {
67410                     var i,
67411                         tracks = get(doc, 'trk'),
67412                         routes = get(doc, 'rte'),
67413                         waypoints = get(doc, 'wpt'),
67414                         // a feature collection
67415                         gj = fc(),
67416                         feature;
67417                     for (i = 0; i < tracks.length; i++) {
67418                         feature = getTrack(tracks[i]);
67419                         if (feature) gj.features.push(feature);
67420                     }
67421                     for (i = 0; i < routes.length; i++) {
67422                         feature = getRoute(routes[i]);
67423                         if (feature) gj.features.push(feature);
67424                     }
67425                     for (i = 0; i < waypoints.length; i++) {
67426                         gj.features.push(getPoint(waypoints[i]));
67427                     }
67428                     function getPoints(node, pointname) {
67429                         var pts = get(node, pointname),
67430                             line = [],
67431                             times = [],
67432                             heartRates = [],
67433                             l = pts.length;
67434                         if (l < 2) return {};  // Invalid line in GeoJSON
67435                         for (var i = 0; i < l; i++) {
67436                             var c = coordPair(pts[i]);
67437                             line.push(c.coordinates);
67438                             if (c.time) times.push(c.time);
67439                             if (c.heartRate) heartRates.push(c.heartRate);
67440                         }
67441                         return {
67442                             line: line,
67443                             times: times,
67444                             heartRates: heartRates
67445                         };
67446                     }
67447                     function getTrack(node) {
67448                         var segments = get(node, 'trkseg'),
67449                             track = [],
67450                             times = [],
67451                             heartRates = [],
67452                             line;
67453                         for (var i = 0; i < segments.length; i++) {
67454                             line = getPoints(segments[i], 'trkpt');
67455                             if (line) {
67456                                 if (line.line) track.push(line.line);
67457                                 if (line.times && line.times.length) times.push(line.times);
67458                                 if (line.heartRates && line.heartRates.length) heartRates.push(line.heartRates);
67459                             }
67460                         }
67461                         if (track.length === 0) return;
67462                         var properties = getProperties(node);
67463                         extend(properties, getLineStyle(get1(node, 'extensions')));
67464                         if (times.length) properties.coordTimes = track.length === 1 ? times[0] : times;
67465                         if (heartRates.length) properties.heartRates = track.length === 1 ? heartRates[0] : heartRates;
67466                         return {
67467                             type: 'Feature',
67468                             properties: properties,
67469                             geometry: {
67470                                 type: track.length === 1 ? 'LineString' : 'MultiLineString',
67471                                 coordinates: track.length === 1 ? track[0] : track
67472                             }
67473                         };
67474                     }
67475                     function getRoute(node) {
67476                         var line = getPoints(node, 'rtept');
67477                         if (!line.line) return;
67478                         var prop = getProperties(node);
67479                         extend(prop, getLineStyle(get1(node, 'extensions')));
67480                         var routeObj = {
67481                             type: 'Feature',
67482                             properties: prop,
67483                             geometry: {
67484                                 type: 'LineString',
67485                                 coordinates: line.line
67486                             }
67487                         };
67488                         return routeObj;
67489                     }
67490                     function getPoint(node) {
67491                         var prop = getProperties(node);
67492                         extend(prop, getMulti(node, ['sym']));
67493                         return {
67494                             type: 'Feature',
67495                             properties: prop,
67496                             geometry: {
67497                                 type: 'Point',
67498                                 coordinates: coordPair(node).coordinates
67499                             }
67500                         };
67501                     }
67502                     function getLineStyle(extensions) {
67503                         var style = {};
67504                         if (extensions) {
67505                             var lineStyle = get1(extensions, 'line');
67506                             if (lineStyle) {
67507                                 var color = nodeVal(get1(lineStyle, 'color')),
67508                                     opacity = parseFloat(nodeVal(get1(lineStyle, 'opacity'))),
67509                                     width = parseFloat(nodeVal(get1(lineStyle, 'width')));
67510                                 if (color) style.stroke = color;
67511                                 if (!isNaN(opacity)) style['stroke-opacity'] = opacity;
67512                                 // GPX width is in mm, convert to px with 96 px per inch
67513                                 if (!isNaN(width)) style['stroke-width'] = width * 96 / 25.4;
67514                             }
67515                         }
67516                         return style;
67517                     }
67518                     function getProperties(node) {
67519                         var prop = getMulti(node, ['name', 'cmt', 'desc', 'type', 'time', 'keywords']),
67520                             links = get(node, 'link');
67521                         if (links.length) prop.links = [];
67522                         for (var i = 0, link; i < links.length; i++) {
67523                             link = { href: attr(links[i], 'href') };
67524                             extend(link, getMulti(links[i], ['text', 'type']));
67525                             prop.links.push(link);
67526                         }
67527                         return prop;
67528                     }
67529                     return gj;
67530                 }
67531             };
67532             return t;
67533         })();
67534
67535         module.exports = toGeoJSON;
67536         });
67537
67538         var _initialized = false;
67539         var _enabled = false;
67540         var _geojson;
67541
67542
67543         function svgData(projection, context, dispatch) {
67544             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
67545             var _showLabels = true;
67546             var detected = utilDetect();
67547             var layer = select(null);
67548             var _vtService;
67549             var _fileList;
67550             var _template;
67551             var _src;
67552
67553
67554             function init() {
67555                 if (_initialized) return;  // run once
67556
67557                 _geojson = {};
67558                 _enabled = true;
67559
67560                 function over() {
67561                     event.stopPropagation();
67562                     event.preventDefault();
67563                     event.dataTransfer.dropEffect = 'copy';
67564                 }
67565
67566                 context.container()
67567                     .attr('dropzone', 'copy')
67568                     .on('drop.svgData', function() {
67569                         event.stopPropagation();
67570                         event.preventDefault();
67571                         if (!detected.filedrop) return;
67572                         drawData.fileList(event.dataTransfer.files);
67573                     })
67574                     .on('dragenter.svgData', over)
67575                     .on('dragexit.svgData', over)
67576                     .on('dragover.svgData', over);
67577
67578                 _initialized = true;
67579             }
67580
67581
67582             function getService() {
67583                 if (services.vectorTile && !_vtService) {
67584                     _vtService = services.vectorTile;
67585                     _vtService.event.on('loadedData', throttledRedraw);
67586                 } else if (!services.vectorTile && _vtService) {
67587                     _vtService = null;
67588                 }
67589
67590                 return _vtService;
67591             }
67592
67593
67594             function showLayer() {
67595                 layerOn();
67596
67597                 layer
67598                     .style('opacity', 0)
67599                     .transition()
67600                     .duration(250)
67601                     .style('opacity', 1)
67602                     .on('end', function () { dispatch.call('change'); });
67603             }
67604
67605
67606             function hideLayer() {
67607                 throttledRedraw.cancel();
67608
67609                 layer
67610                     .transition()
67611                     .duration(250)
67612                     .style('opacity', 0)
67613                     .on('end', layerOff);
67614             }
67615
67616
67617             function layerOn() {
67618                 layer.style('display', 'block');
67619             }
67620
67621
67622             function layerOff() {
67623                 layer.selectAll('.viewfield-group').remove();
67624                 layer.style('display', 'none');
67625             }
67626
67627
67628             // ensure that all geojson features in a collection have IDs
67629             function ensureIDs(gj) {
67630                 if (!gj) return null;
67631
67632                 if (gj.type === 'FeatureCollection') {
67633                     for (var i = 0; i < gj.features.length; i++) {
67634                         ensureFeatureID(gj.features[i]);
67635                     }
67636                 } else {
67637                     ensureFeatureID(gj);
67638                 }
67639                 return gj;
67640             }
67641
67642
67643             // ensure that each single Feature object has a unique ID
67644             function ensureFeatureID(feature) {
67645                 if (!feature) return;
67646                 feature.__featurehash__ = utilHashcode(fastJsonStableStringify(feature));
67647                 return feature;
67648             }
67649
67650
67651             // Prefer an array of Features instead of a FeatureCollection
67652             function getFeatures(gj) {
67653                 if (!gj) return [];
67654
67655                 if (gj.type === 'FeatureCollection') {
67656                     return gj.features;
67657                 } else {
67658                     return [gj];
67659                 }
67660             }
67661
67662
67663             function featureKey(d) {
67664                 return d.__featurehash__;
67665             }
67666
67667
67668             function isPolygon(d) {
67669                 return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon';
67670             }
67671
67672
67673             function clipPathID(d) {
67674                 return 'ideditor-data-' + d.__featurehash__ + '-clippath';
67675             }
67676
67677
67678             function featureClasses(d) {
67679                 return [
67680                     'data' + d.__featurehash__,
67681                     d.geometry.type,
67682                     isPolygon(d) ? 'area' : '',
67683                     d.__layerID__ || ''
67684                 ].filter(Boolean).join(' ');
67685             }
67686
67687
67688             function drawData(selection) {
67689                 var vtService = getService();
67690                 var getPath = svgPath(projection).geojson;
67691                 var getAreaPath = svgPath(projection, null, true).geojson;
67692                 var hasData = drawData.hasData();
67693
67694                 layer = selection.selectAll('.layer-mapdata')
67695                     .data(_enabled && hasData ? [0] : []);
67696
67697                 layer.exit()
67698                     .remove();
67699
67700                 layer = layer.enter()
67701                     .append('g')
67702                     .attr('class', 'layer-mapdata')
67703                     .merge(layer);
67704
67705                 var surface = context.surface();
67706                 if (!surface || surface.empty()) return;  // not ready to draw yet, starting up
67707
67708
67709                 // Gather data
67710                 var geoData, polygonData;
67711                 if (_template && vtService) {   // fetch data from vector tile service
67712                     var sourceID = _template;
67713                     vtService.loadTiles(sourceID, _template, projection);
67714                     geoData = vtService.data(sourceID, projection);
67715                 } else {
67716                     geoData = getFeatures(_geojson);
67717                 }
67718                 geoData = geoData.filter(getPath);
67719                 polygonData = geoData.filter(isPolygon);
67720
67721
67722                 // Draw clip paths for polygons
67723                 var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data')
67724                    .data(polygonData, featureKey);
67725
67726                 clipPaths.exit()
67727                    .remove();
67728
67729                 var clipPathsEnter = clipPaths.enter()
67730                    .append('clipPath')
67731                    .attr('class', 'clipPath-data')
67732                    .attr('id', clipPathID);
67733
67734                 clipPathsEnter
67735                    .append('path');
67736
67737                 clipPaths.merge(clipPathsEnter)
67738                    .selectAll('path')
67739                    .attr('d', getAreaPath);
67740
67741
67742                 // Draw fill, shadow, stroke layers
67743                 var datagroups = layer
67744                     .selectAll('g.datagroup')
67745                     .data(['fill', 'shadow', 'stroke']);
67746
67747                 datagroups = datagroups.enter()
67748                     .append('g')
67749                     .attr('class', function(d) { return 'datagroup datagroup-' + d; })
67750                     .merge(datagroups);
67751
67752
67753                 // Draw paths
67754                 var pathData = {
67755                     fill: polygonData,
67756                     shadow: geoData,
67757                     stroke: geoData
67758                 };
67759
67760                 var paths = datagroups
67761                     .selectAll('path')
67762                     .data(function(layer) { return pathData[layer]; }, featureKey);
67763
67764                 // exit
67765                 paths.exit()
67766                     .remove();
67767
67768                 // enter/update
67769                 paths = paths.enter()
67770                     .append('path')
67771                     .attr('class', function(d) {
67772                         var datagroup = this.parentNode.__data__;
67773                         return 'pathdata ' + datagroup + ' ' + featureClasses(d);
67774                     })
67775                     .attr('clip-path', function(d) {
67776                         var datagroup = this.parentNode.__data__;
67777                         return datagroup === 'fill' ? ('url(#' + clipPathID(d) + ')') : null;
67778                     })
67779                     .merge(paths)
67780                     .attr('d', function(d) {
67781                         var datagroup = this.parentNode.__data__;
67782                         return datagroup === 'fill' ? getAreaPath(d) : getPath(d);
67783                     });
67784
67785
67786                 // Draw labels
67787                 layer
67788                     .call(drawLabels, 'label-halo', geoData)
67789                     .call(drawLabels, 'label', geoData);
67790
67791
67792                 function drawLabels(selection, textClass, data) {
67793                     var labelPath = d3_geoPath(projection);
67794                     var labelData = data.filter(function(d) {
67795                         return _showLabels && d.properties && (d.properties.desc || d.properties.name);
67796                     });
67797
67798                     var labels = selection.selectAll('text.' + textClass)
67799                         .data(labelData, featureKey);
67800
67801                     // exit
67802                     labels.exit()
67803                         .remove();
67804
67805                     // enter/update
67806                     labels = labels.enter()
67807                         .append('text')
67808                         .attr('class', function(d) { return textClass + ' ' + featureClasses(d); })
67809                         .merge(labels)
67810                         .text(function(d) {
67811                             return d.properties.desc || d.properties.name;
67812                         })
67813                         .attr('x', function(d) {
67814                             var centroid = labelPath.centroid(d);
67815                             return centroid[0] + 11;
67816                         })
67817                         .attr('y', function(d) {
67818                             var centroid = labelPath.centroid(d);
67819                             return centroid[1];
67820                         });
67821                 }
67822             }
67823
67824
67825             function getExtension(fileName) {
67826                 if (!fileName) return;
67827
67828                 var re = /\.(gpx|kml|(geo)?json)$/i;
67829                 var match = fileName.toLowerCase().match(re);
67830                 return match && match.length && match[0];
67831             }
67832
67833
67834             function xmlToDom(textdata) {
67835                 return (new DOMParser()).parseFromString(textdata, 'text/xml');
67836             }
67837
67838
67839             drawData.setFile = function(extension, data) {
67840                 _template = null;
67841                 _fileList = null;
67842                 _geojson = null;
67843                 _src = null;
67844
67845                 var gj;
67846                 switch (extension) {
67847                     case '.gpx':
67848                         gj = togeojson.gpx(xmlToDom(data));
67849                         break;
67850                     case '.kml':
67851                         gj = togeojson.kml(xmlToDom(data));
67852                         break;
67853                     case '.geojson':
67854                     case '.json':
67855                         gj = JSON.parse(data);
67856                         break;
67857                 }
67858
67859                 gj = gj || {};
67860                 if (Object.keys(gj).length) {
67861                     _geojson = ensureIDs(gj);
67862                     _src = extension + ' data file';
67863                     this.fitZoom();
67864                 }
67865
67866                 dispatch.call('change');
67867                 return this;
67868             };
67869
67870
67871             drawData.showLabels = function(val) {
67872                 if (!arguments.length) return _showLabels;
67873
67874                 _showLabels = val;
67875                 return this;
67876             };
67877
67878
67879             drawData.enabled = function(val) {
67880                 if (!arguments.length) return _enabled;
67881
67882                 _enabled = val;
67883                 if (_enabled) {
67884                     showLayer();
67885                 } else {
67886                     hideLayer();
67887                 }
67888
67889                 dispatch.call('change');
67890                 return this;
67891             };
67892
67893
67894             drawData.hasData = function() {
67895                 var gj = _geojson || {};
67896                 return !!(_template || Object.keys(gj).length);
67897             };
67898
67899
67900             drawData.template = function(val, src) {
67901                 if (!arguments.length) return _template;
67902
67903                 // test source against OSM imagery blacklists..
67904                 var osm = context.connection();
67905                 if (osm) {
67906                     var blacklists = osm.imageryBlacklists();
67907                     var fail = false;
67908                     var tested = 0;
67909                     var regex;
67910
67911                     for (var i = 0; i < blacklists.length; i++) {
67912                         try {
67913                             regex = new RegExp(blacklists[i]);
67914                             fail = regex.test(val);
67915                             tested++;
67916                             if (fail) break;
67917                         } catch (e) {
67918                             /* noop */
67919                         }
67920                     }
67921
67922                     // ensure at least one test was run.
67923                     if (!tested) {
67924                         regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*');
67925                         fail = regex.test(val);
67926                     }
67927                 }
67928
67929                 _template = val;
67930                 _fileList = null;
67931                 _geojson = null;
67932
67933                 // strip off the querystring/hash from the template,
67934                 // it often includes the access token
67935                 _src = src || ('vectortile:' + val.split(/[?#]/)[0]);
67936
67937                 dispatch.call('change');
67938                 return this;
67939             };
67940
67941
67942             drawData.geojson = function(gj, src) {
67943                 if (!arguments.length) return _geojson;
67944
67945                 _template = null;
67946                 _fileList = null;
67947                 _geojson = null;
67948                 _src = null;
67949
67950                 gj = gj || {};
67951                 if (Object.keys(gj).length) {
67952                     _geojson = ensureIDs(gj);
67953                     _src = src || 'unknown.geojson';
67954                 }
67955
67956                 dispatch.call('change');
67957                 return this;
67958             };
67959
67960
67961             drawData.fileList = function(fileList) {
67962                 if (!arguments.length) return _fileList;
67963
67964                 _template = null;
67965                 _fileList = fileList;
67966                 _geojson = null;
67967                 _src = null;
67968
67969                 if (!fileList || !fileList.length) return this;
67970                 var f = fileList[0];
67971                 var extension = getExtension(f.name);
67972                 var reader = new FileReader();
67973                 reader.onload = (function() {
67974                     return function(e) {
67975                         drawData.setFile(extension, e.target.result);
67976                     };
67977                 })();
67978
67979                 reader.readAsText(f);
67980
67981                 return this;
67982             };
67983
67984
67985             drawData.url = function(url, defaultExtension) {
67986                 _template = null;
67987                 _fileList = null;
67988                 _geojson = null;
67989                 _src = null;
67990
67991                 // strip off any querystring/hash from the url before checking extension
67992                 var testUrl = url.split(/[?#]/)[0];
67993                 var extension = getExtension(testUrl) || defaultExtension;
67994                 if (extension) {
67995                     _template = null;
67996                     d3_text(url)
67997                         .then(function(data) {
67998                             drawData.setFile(extension, data);
67999                         })
68000                         .catch(function() {
68001                             /* ignore */
68002                         });
68003
68004                 } else {
68005                     drawData.template(url);
68006                 }
68007
68008                 return this;
68009             };
68010
68011
68012             drawData.getSrc = function() {
68013                 return _src || '';
68014             };
68015
68016
68017             drawData.fitZoom = function() {
68018                 var features = getFeatures(_geojson);
68019                 if (!features.length) return;
68020
68021                 var map = context.map();
68022                 var viewport = map.trimmedExtent().polygon();
68023                 var coords = features.reduce(function(coords, feature) {
68024                     var geom = feature.geometry;
68025                     if (!geom) return coords;
68026
68027                     var c = geom.coordinates;
68028
68029                     /* eslint-disable no-fallthrough */
68030                     switch (geom.type) {
68031                         case 'Point':
68032                             c = [c];
68033                         case 'MultiPoint':
68034                         case 'LineString':
68035                             break;
68036
68037                         case 'MultiPolygon':
68038                             c = utilArrayFlatten(c);
68039                         case 'Polygon':
68040                         case 'MultiLineString':
68041                             c = utilArrayFlatten(c);
68042                             break;
68043                     }
68044                     /* eslint-enable no-fallthrough */
68045
68046                     return utilArrayUnion(coords, c);
68047                 }, []);
68048
68049                 if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
68050                     var extent = geoExtent(d3_geoBounds({ type: 'LineString', coordinates: coords }));
68051                     map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
68052                 }
68053
68054                 return this;
68055             };
68056
68057
68058             init();
68059             return drawData;
68060         }
68061
68062         function svgDebug(projection, context) {
68063
68064           function drawDebug(selection) {
68065             const showTile = context.getDebug('tile');
68066             const showCollision = context.getDebug('collision');
68067             const showImagery = context.getDebug('imagery');
68068             const showTouchTargets = context.getDebug('target');
68069             const showDownloaded = context.getDebug('downloaded');
68070
68071             let debugData = [];
68072             if (showTile) {
68073               debugData.push({ class: 'red', label: 'tile' });
68074             }
68075             if (showCollision) {
68076               debugData.push({ class: 'yellow', label: 'collision' });
68077             }
68078             if (showImagery) {
68079               debugData.push({ class: 'orange', label: 'imagery' });
68080             }
68081             if (showTouchTargets) {
68082               debugData.push({ class: 'pink', label: 'touchTargets' });
68083             }
68084             if (showDownloaded) {
68085               debugData.push({ class: 'purple', label: 'downloaded' });
68086             }
68087
68088
68089             let legend = context.container().select('.main-content')
68090               .selectAll('.debug-legend')
68091               .data(debugData.length ? [0] : []);
68092
68093             legend.exit()
68094               .remove();
68095
68096             legend = legend.enter()
68097               .append('div')
68098               .attr('class', 'fillD debug-legend')
68099               .merge(legend);
68100
68101
68102             let legendItems = legend.selectAll('.debug-legend-item')
68103               .data(debugData, d => d.label);
68104
68105             legendItems.exit()
68106               .remove();
68107
68108             legendItems.enter()
68109               .append('span')
68110               .attr('class', d => `debug-legend-item ${d.class}`)
68111               .text(d => d.label);
68112
68113
68114             let layer = selection.selectAll('.layer-debug')
68115               .data(showImagery || showDownloaded ? [0] : []);
68116
68117             layer.exit()
68118               .remove();
68119
68120             layer = layer.enter()
68121               .append('g')
68122               .attr('class', 'layer-debug')
68123               .merge(layer);
68124
68125
68126             // imagery
68127             const extent = context.map().extent();
68128             _mainFileFetcher.get('imagery')
68129               .then(d => {
68130                 const hits = (showImagery && d.query.bbox(extent.rectangle(), true)) || [];
68131                 const features = hits.map(d => d.features[d.id]);
68132
68133                 let imagery = layer.selectAll('path.debug-imagery')
68134                   .data(features);
68135
68136                 imagery.exit()
68137                   .remove();
68138
68139                 imagery.enter()
68140                   .append('path')
68141                   .attr('class', 'debug-imagery debug orange');
68142               })
68143               .catch(() => { /* ignore */ });
68144
68145             // downloaded
68146             const osm = context.connection();
68147             let dataDownloaded = [];
68148             if (osm && showDownloaded) {
68149               const rtree = osm.caches('get').tile.rtree;
68150               dataDownloaded = rtree.all().map(bbox => {
68151                 return {
68152                   type: 'Feature',
68153                   properties: { id: bbox.id },
68154                   geometry: {
68155                     type: 'Polygon',
68156                     coordinates: [[
68157                       [ bbox.minX, bbox.minY ],
68158                       [ bbox.minX, bbox.maxY ],
68159                       [ bbox.maxX, bbox.maxY ],
68160                       [ bbox.maxX, bbox.minY ],
68161                       [ bbox.minX, bbox.minY ]
68162                     ]]
68163                   }
68164                 };
68165               });
68166             }
68167
68168             let downloaded = layer
68169               .selectAll('path.debug-downloaded')
68170               .data(showDownloaded ? dataDownloaded : []);
68171
68172             downloaded.exit()
68173               .remove();
68174
68175             downloaded.enter()
68176               .append('path')
68177               .attr('class', 'debug-downloaded debug purple');
68178
68179             // update
68180             layer.selectAll('path')
68181               .attr('d', svgPath(projection).geojson);
68182           }
68183
68184
68185           // This looks strange because `enabled` methods on other layers are
68186           // chainable getter/setters, and this one is just a getter.
68187           drawDebug.enabled = function() {
68188             if (!arguments.length) {
68189               return context.getDebug('tile') ||
68190                 context.getDebug('collision') ||
68191                 context.getDebug('imagery') ||
68192                 context.getDebug('target') ||
68193                 context.getDebug('downloaded');
68194             } else {
68195                 return this;
68196             }
68197           };
68198
68199
68200           return drawDebug;
68201         }
68202
68203         let _layerEnabled = false;
68204         let _qaService;
68205
68206         function svgKeepRight(projection, context, dispatch) {
68207           const throttledRedraw = throttle(() => dispatch.call('change'), 1000);
68208           const minZoom = 12;
68209
68210           let touchLayer = select(null);
68211           let drawLayer = select(null);
68212           let layerVisible = false;
68213
68214           function markerPath(selection, klass) {
68215             selection
68216               .attr('class', klass)
68217               .attr('transform', 'translate(-4, -24)')
68218               .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');
68219           }
68220
68221           // Loosely-coupled keepRight service for fetching issues.
68222           function getService() {
68223             if (services.keepRight && !_qaService) {
68224               _qaService = services.keepRight;
68225               _qaService.on('loaded', throttledRedraw);
68226             } else if (!services.keepRight && _qaService) {
68227               _qaService = null;
68228             }
68229
68230             return _qaService;
68231           }
68232
68233           // Show the markers
68234           function editOn() {
68235             if (!layerVisible) {
68236               layerVisible = true;
68237               drawLayer
68238                 .style('display', 'block');
68239             }
68240           }
68241
68242           // Immediately remove the markers and their touch targets
68243           function editOff() {
68244             if (layerVisible) {
68245               layerVisible = false;
68246               drawLayer
68247                 .style('display', 'none');
68248               drawLayer.selectAll('.qaItem.keepRight')
68249                 .remove();
68250               touchLayer.selectAll('.qaItem.keepRight')
68251                 .remove();
68252             }
68253           }
68254
68255           // Enable the layer.  This shows the markers and transitions them to visible.
68256           function layerOn() {
68257             editOn();
68258
68259             drawLayer
68260               .style('opacity', 0)
68261               .transition()
68262               .duration(250)
68263               .style('opacity', 1)
68264               .on('end interrupt', () => dispatch.call('change'));
68265           }
68266
68267           // Disable the layer.  This transitions the layer invisible and then hides the markers.
68268           function layerOff() {
68269             throttledRedraw.cancel();
68270             drawLayer.interrupt();
68271             touchLayer.selectAll('.qaItem.keepRight')
68272               .remove();
68273
68274             drawLayer
68275               .transition()
68276               .duration(250)
68277               .style('opacity', 0)
68278               .on('end interrupt', () => {
68279                 editOff();
68280                 dispatch.call('change');
68281               });
68282           }
68283
68284           // Update the issue markers
68285           function updateMarkers() {
68286             if (!layerVisible || !_layerEnabled) return;
68287
68288             const service = getService();
68289             const selectedID = context.selectedErrorID();
68290             const data = (service ? service.getItems(projection) : []);
68291             const getTransform = svgPointTransform(projection);
68292
68293             // Draw markers..
68294             const markers = drawLayer.selectAll('.qaItem.keepRight')
68295               .data(data, d => d.id);
68296
68297             // exit
68298             markers.exit()
68299               .remove();
68300
68301             // enter
68302             const markersEnter = markers.enter()
68303               .append('g')
68304                 .attr('class', d => `qaItem ${d.service} itemId-${d.id} itemType-${d.parentIssueType}`);
68305
68306             markersEnter
68307               .append('ellipse')
68308                 .attr('cx', 0.5)
68309                 .attr('cy', 1)
68310                 .attr('rx', 6.5)
68311                 .attr('ry', 3)
68312                 .attr('class', 'stroke');
68313
68314             markersEnter
68315               .append('path')
68316                 .call(markerPath, 'shadow');
68317
68318             markersEnter
68319               .append('use')
68320                 .attr('class', 'qaItem-fill')
68321                 .attr('width', '20px')
68322                 .attr('height', '20px')
68323                 .attr('x', '-8px')
68324                 .attr('y', '-22px')
68325                 .attr('xlink:href', '#iD-icon-bolt');
68326
68327             // update
68328             markers
68329               .merge(markersEnter)
68330               .sort(sortY)
68331                 .classed('selected', d => d.id === selectedID)
68332                 .attr('transform', getTransform);
68333
68334
68335             // Draw targets..
68336             if (touchLayer.empty()) return;
68337             const fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
68338
68339             const targets = touchLayer.selectAll('.qaItem.keepRight')
68340               .data(data, d => d.id);
68341
68342             // exit
68343             targets.exit()
68344               .remove();
68345
68346             // enter/update
68347             targets.enter()
68348               .append('rect')
68349                 .attr('width', '20px')
68350                 .attr('height', '20px')
68351                 .attr('x', '-8px')
68352                 .attr('y', '-22px')
68353               .merge(targets)
68354               .sort(sortY)
68355                 .attr('class', d => `qaItem ${d.service} target ${fillClass} itemId-${d.id}`)
68356                 .attr('transform', getTransform);
68357
68358
68359             function sortY(a, b) {
68360               return (a.id === selectedID) ? 1
68361                 : (b.id === selectedID) ? -1
68362                 : (a.severity === 'error' && b.severity !== 'error') ? 1
68363                 : (b.severity === 'error' && a.severity !== 'error') ? -1
68364                 : b.loc[1] - a.loc[1];
68365             }
68366           }
68367
68368           // Draw the keepRight layer and schedule loading issues and updating markers.
68369           function drawKeepRight(selection) {
68370             const service = getService();
68371
68372             const surface = context.surface();
68373             if (surface && !surface.empty()) {
68374               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
68375             }
68376
68377             drawLayer = selection.selectAll('.layer-keepRight')
68378               .data(service ? [0] : []);
68379
68380             drawLayer.exit()
68381               .remove();
68382
68383             drawLayer = drawLayer.enter()
68384               .append('g')
68385                 .attr('class', 'layer-keepRight')
68386                 .style('display', _layerEnabled ? 'block' : 'none')
68387               .merge(drawLayer);
68388
68389             if (_layerEnabled) {
68390               if (service && ~~context.map().zoom() >= minZoom) {
68391                 editOn();
68392                 service.loadIssues(projection);
68393                 updateMarkers();
68394               } else {
68395                 editOff();
68396               }
68397             }
68398           }
68399
68400           // Toggles the layer on and off
68401           drawKeepRight.enabled = function(val) {
68402             if (!arguments.length) return _layerEnabled;
68403
68404             _layerEnabled = val;
68405             if (_layerEnabled) {
68406               layerOn();
68407             } else {
68408               layerOff();
68409               if (context.selectedErrorID()) {
68410                 context.enter(modeBrowse(context));
68411               }
68412             }
68413
68414             dispatch.call('change');
68415             return this;
68416           };
68417
68418           drawKeepRight.supported = () => !!getService();
68419
68420           return drawKeepRight;
68421         }
68422
68423         function svgGeolocate(projection) {
68424             var layer = select(null);
68425             var _position;
68426
68427
68428             function init() {
68429                 if (svgGeolocate.initialized) return;  // run once
68430                 svgGeolocate.enabled = false;
68431                 svgGeolocate.initialized = true;
68432             }
68433
68434             function showLayer() {
68435                 layer.style('display', 'block');
68436             }
68437
68438
68439             function hideLayer() {
68440                 layer
68441                     .transition()
68442                     .duration(250)
68443                     .style('opacity', 0);
68444             }
68445
68446             function layerOn() {
68447                 layer
68448                     .style('opacity', 0)
68449                     .transition()
68450                     .duration(250)
68451                     .style('opacity', 1);
68452
68453             }
68454
68455             function layerOff() {
68456                 layer.style('display', 'none');
68457             }
68458
68459             function transform(d) {
68460                 return svgPointTransform(projection)(d);
68461             }
68462
68463             function accuracy(accuracy, loc) { // converts accuracy to pixels...
68464                 var degreesRadius = geoMetersToLat(accuracy),
68465                     tangentLoc = [loc[0], loc[1] + degreesRadius],
68466                     projectedTangent = projection(tangentLoc),
68467                     projectedLoc = projection([loc[0], loc[1]]);
68468
68469                 // southern most point will have higher pixel value...
68470                return Math.round(projectedLoc[1] - projectedTangent[1]).toString();
68471             }
68472
68473             function update() {
68474                 var geolocation = { loc: [_position.coords.longitude, _position.coords.latitude] };
68475
68476                 var groups = layer.selectAll('.geolocations').selectAll('.geolocation')
68477                     .data([geolocation]);
68478
68479                 groups.exit()
68480                     .remove();
68481
68482                 var pointsEnter = groups.enter()
68483                     .append('g')
68484                     .attr('class', 'geolocation');
68485
68486                 pointsEnter
68487                     .append('circle')
68488                     .attr('class', 'geolocate-radius')
68489                     .attr('dx', '0')
68490                     .attr('dy', '0')
68491                     .attr('fill', 'rgb(15,128,225)')
68492                     .attr('fill-opacity', '0.3')
68493                     .attr('r', '0');
68494
68495                 pointsEnter
68496                     .append('circle')
68497                     .attr('dx', '0')
68498                     .attr('dy', '0')
68499                     .attr('fill', 'rgb(15,128,225)')
68500                     .attr('stroke', 'white')
68501                     .attr('stroke-width', '1.5')
68502                     .attr('r', '6');
68503
68504                 groups.merge(pointsEnter)
68505                     .attr('transform', transform);
68506
68507                 layer.select('.geolocate-radius').attr('r', accuracy(_position.coords.accuracy, geolocation.loc));
68508             }
68509
68510             function drawLocation(selection) {
68511                 var enabled = svgGeolocate.enabled;
68512
68513                 layer = selection.selectAll('.layer-geolocate')
68514                     .data([0]);
68515
68516                 layer.exit()
68517                     .remove();
68518
68519                 var layerEnter = layer.enter()
68520                     .append('g')
68521                     .attr('class', 'layer-geolocate')
68522                     .style('display', enabled ? 'block' : 'none');
68523
68524                 layerEnter
68525                     .append('g')
68526                     .attr('class', 'geolocations');
68527
68528                 layer = layerEnter
68529                     .merge(layer);
68530
68531                 if (enabled) {
68532                     update();
68533                 } else {
68534                     layerOff();
68535                 }
68536             }
68537
68538             drawLocation.enabled = function (position, enabled) {
68539                 if (!arguments.length) return svgGeolocate.enabled;
68540                 _position = position;
68541                 svgGeolocate.enabled = enabled;
68542                 if (svgGeolocate.enabled) {
68543                     showLayer();
68544                     layerOn();
68545                 } else {
68546                     hideLayer();
68547                 }
68548                 return this;
68549             };
68550
68551             init();
68552             return drawLocation;
68553         }
68554
68555         function svgLabels(projection, context) {
68556             var path = d3_geoPath(projection);
68557             var detected = utilDetect();
68558             var baselineHack = (detected.ie ||
68559                 detected.browser.toLowerCase() === 'edge' ||
68560                 (detected.browser.toLowerCase() === 'firefox' && detected.version >= 70));
68561             var _rdrawn = new RBush();
68562             var _rskipped = new RBush();
68563             var _textWidthCache = {};
68564             var _entitybboxes = {};
68565
68566             // Listed from highest to lowest priority
68567             var labelStack = [
68568                 ['line', 'aeroway', '*', 12],
68569                 ['line', 'highway', 'motorway', 12],
68570                 ['line', 'highway', 'trunk', 12],
68571                 ['line', 'highway', 'primary', 12],
68572                 ['line', 'highway', 'secondary', 12],
68573                 ['line', 'highway', 'tertiary', 12],
68574                 ['line', 'highway', '*', 12],
68575                 ['line', 'railway', '*', 12],
68576                 ['line', 'waterway', '*', 12],
68577                 ['area', 'aeroway', '*', 12],
68578                 ['area', 'amenity', '*', 12],
68579                 ['area', 'building', '*', 12],
68580                 ['area', 'historic', '*', 12],
68581                 ['area', 'leisure', '*', 12],
68582                 ['area', 'man_made', '*', 12],
68583                 ['area', 'natural', '*', 12],
68584                 ['area', 'shop', '*', 12],
68585                 ['area', 'tourism', '*', 12],
68586                 ['area', 'camp_site', '*', 12],
68587                 ['point', 'aeroway', '*', 10],
68588                 ['point', 'amenity', '*', 10],
68589                 ['point', 'building', '*', 10],
68590                 ['point', 'historic', '*', 10],
68591                 ['point', 'leisure', '*', 10],
68592                 ['point', 'man_made', '*', 10],
68593                 ['point', 'natural', '*', 10],
68594                 ['point', 'shop', '*', 10],
68595                 ['point', 'tourism', '*', 10],
68596                 ['point', 'camp_site', '*', 10],
68597                 ['line', 'name', '*', 12],
68598                 ['area', 'name', '*', 12],
68599                 ['point', 'name', '*', 10]
68600             ];
68601
68602
68603             function shouldSkipIcon(preset) {
68604                 var noIcons = ['building', 'landuse', 'natural'];
68605                 return noIcons.some(function(s) {
68606                     return preset.id.indexOf(s) >= 0;
68607                 });
68608             }
68609
68610
68611             function get(array, prop) {
68612                 return function(d, i) { return array[i][prop]; };
68613             }
68614
68615
68616             function textWidth(text, size, elem) {
68617                 var c = _textWidthCache[size];
68618                 if (!c) c = _textWidthCache[size] = {};
68619
68620                 if (c[text]) {
68621                     return c[text];
68622
68623                 } else if (elem) {
68624                     c[text] = elem.getComputedTextLength();
68625                     return c[text];
68626
68627                 } else {
68628                     var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
68629                     if (str === null) {
68630                         return size / 3 * 2 * text.length;
68631                     } else {
68632                         return size / 3 * (2 * text.length + str.length);
68633                     }
68634                 }
68635             }
68636
68637
68638             function drawLinePaths(selection, entities, filter, classes, labels) {
68639                 var paths = selection.selectAll('path')
68640                     .filter(filter)
68641                     .data(entities, osmEntity.key);
68642
68643                 // exit
68644                 paths.exit()
68645                     .remove();
68646
68647                 // enter/update
68648                 paths.enter()
68649                     .append('path')
68650                     .style('stroke-width', get(labels, 'font-size'))
68651                     .attr('id', function(d) { return 'ideditor-labelpath-' + d.id; })
68652                     .attr('class', classes)
68653                     .merge(paths)
68654                     .attr('d', get(labels, 'lineString'));
68655             }
68656
68657
68658             function drawLineLabels(selection, entities, filter, classes, labels) {
68659                 var texts = selection.selectAll('text.' + classes)
68660                     .filter(filter)
68661                     .data(entities, osmEntity.key);
68662
68663                 // exit
68664                 texts.exit()
68665                     .remove();
68666
68667                 // enter
68668                 texts.enter()
68669                     .append('text')
68670                     .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
68671                     .attr('dy', baselineHack ? '0.35em' : null)
68672                     .append('textPath')
68673                     .attr('class', 'textpath');
68674
68675                 // update
68676                 selection.selectAll('text.' + classes).selectAll('.textpath')
68677                     .filter(filter)
68678                     .data(entities, osmEntity.key)
68679                     .attr('startOffset', '50%')
68680                     .attr('xlink:href', function(d) { return '#ideditor-labelpath-' + d.id; })
68681                     .text(utilDisplayNameForPath);
68682             }
68683
68684
68685             function drawPointLabels(selection, entities, filter, classes, labels) {
68686                 var texts = selection.selectAll('text.' + classes)
68687                     .filter(filter)
68688                     .data(entities, osmEntity.key);
68689
68690                 // exit
68691                 texts.exit()
68692                     .remove();
68693
68694                 // enter/update
68695                 texts.enter()
68696                     .append('text')
68697                     .attr('class', function(d, i) {
68698                         return classes + ' ' + labels[i].classes + ' ' + d.id;
68699                     })
68700                     .merge(texts)
68701                     .attr('x', get(labels, 'x'))
68702                     .attr('y', get(labels, 'y'))
68703                     .style('text-anchor', get(labels, 'textAnchor'))
68704                     .text(utilDisplayName)
68705                     .each(function(d, i) {
68706                         textWidth(utilDisplayName(d), labels[i].height, this);
68707                     });
68708             }
68709
68710
68711             function drawAreaLabels(selection, entities, filter, classes, labels) {
68712                 entities = entities.filter(hasText);
68713                 labels = labels.filter(hasText);
68714                 drawPointLabels(selection, entities, filter, classes, labels);
68715
68716                 function hasText(d, i) {
68717                     return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
68718                 }
68719             }
68720
68721
68722             function drawAreaIcons(selection, entities, filter, classes, labels) {
68723                 var icons = selection.selectAll('use.' + classes)
68724                     .filter(filter)
68725                     .data(entities, osmEntity.key);
68726
68727                 // exit
68728                 icons.exit()
68729                     .remove();
68730
68731                 // enter/update
68732                 icons.enter()
68733                     .append('use')
68734                     .attr('class', 'icon ' + classes)
68735                     .attr('width', '17px')
68736                     .attr('height', '17px')
68737                     .merge(icons)
68738                     .attr('transform', get(labels, 'transform'))
68739                     .attr('xlink:href', function(d) {
68740                         var preset = _mainPresetIndex.match(d, context.graph());
68741                         var picon = preset && preset.icon;
68742
68743                         if (!picon) {
68744                             return '';
68745                         } else {
68746                             var isMaki = /^maki-/.test(picon);
68747                             return '#' + picon + (isMaki ? '-15' : '');
68748                         }
68749                     });
68750             }
68751
68752
68753             function drawCollisionBoxes(selection, rtree, which) {
68754                 var classes = 'debug ' + which + ' ' + (which === 'debug-skipped' ? 'orange' : 'yellow');
68755
68756                 var gj = [];
68757                 if (context.getDebug('collision')) {
68758                     gj = rtree.all().map(function(d) {
68759                         return { type: 'Polygon', coordinates: [[
68760                             [d.minX, d.minY],
68761                             [d.maxX, d.minY],
68762                             [d.maxX, d.maxY],
68763                             [d.minX, d.maxY],
68764                             [d.minX, d.minY]
68765                         ]]};
68766                     });
68767                 }
68768
68769                 var boxes = selection.selectAll('.' + which)
68770                     .data(gj);
68771
68772                 // exit
68773                 boxes.exit()
68774                     .remove();
68775
68776                 // enter/update
68777                 boxes.enter()
68778                     .append('path')
68779                     .attr('class', classes)
68780                     .merge(boxes)
68781                     .attr('d', d3_geoPath());
68782             }
68783
68784
68785             function drawLabels(selection, graph, entities, filter, dimensions, fullRedraw) {
68786                 var wireframe = context.surface().classed('fill-wireframe');
68787                 var zoom = geoScaleToZoom(projection.scale());
68788
68789                 var labelable = [];
68790                 var renderNodeAs = {};
68791                 var i, j, k, entity, geometry;
68792
68793                 for (i = 0; i < labelStack.length; i++) {
68794                     labelable.push([]);
68795                 }
68796
68797                 if (fullRedraw) {
68798                     _rdrawn.clear();
68799                     _rskipped.clear();
68800                     _entitybboxes = {};
68801
68802                 } else {
68803                     for (i = 0; i < entities.length; i++) {
68804                         entity = entities[i];
68805                         var toRemove = []
68806                             .concat(_entitybboxes[entity.id] || [])
68807                             .concat(_entitybboxes[entity.id + 'I'] || []);
68808
68809                         for (j = 0; j < toRemove.length; j++) {
68810                             _rdrawn.remove(toRemove[j]);
68811                             _rskipped.remove(toRemove[j]);
68812                         }
68813                     }
68814                 }
68815
68816                 // Loop through all the entities to do some preprocessing
68817                 for (i = 0; i < entities.length; i++) {
68818                     entity = entities[i];
68819                     geometry = entity.geometry(graph);
68820
68821                     // Insert collision boxes around interesting points/vertices
68822                     if (geometry === 'point' || (geometry === 'vertex' && isInterestingVertex(entity))) {
68823                         var hasDirections = entity.directions(graph, projection).length;
68824                         var markerPadding;
68825
68826                         if (!wireframe && geometry === 'point' && !(zoom >= 18 && hasDirections)) {
68827                             renderNodeAs[entity.id] = 'point';
68828                             markerPadding = 20;   // extra y for marker height
68829                         } else {
68830                             renderNodeAs[entity.id] = 'vertex';
68831                             markerPadding = 0;
68832                         }
68833
68834                         var coord = projection(entity.loc);
68835                         var nodePadding = 10;
68836                         var bbox = {
68837                             minX: coord[0] - nodePadding,
68838                             minY: coord[1] - nodePadding - markerPadding,
68839                             maxX: coord[0] + nodePadding,
68840                             maxY: coord[1] + nodePadding
68841                         };
68842
68843                         doInsert(bbox, entity.id + 'P');
68844                     }
68845
68846                     // From here on, treat vertices like points
68847                     if (geometry === 'vertex') {
68848                         geometry = 'point';
68849                     }
68850
68851                     // Determine which entities are label-able
68852                     var preset = geometry === 'area' && _mainPresetIndex.match(entity, graph);
68853                     var icon = preset && !shouldSkipIcon(preset) && preset.icon;
68854
68855                     if (!icon && !utilDisplayName(entity))
68856                         continue;
68857
68858                     for (k = 0; k < labelStack.length; k++) {
68859                         var matchGeom = labelStack[k][0];
68860                         var matchKey = labelStack[k][1];
68861                         var matchVal = labelStack[k][2];
68862                         var hasVal = entity.tags[matchKey];
68863
68864                         if (geometry === matchGeom && hasVal && (matchVal === '*' || matchVal === hasVal)) {
68865                             labelable[k].push(entity);
68866                             break;
68867                         }
68868                     }
68869                 }
68870
68871                 var positions = {
68872                     point: [],
68873                     line: [],
68874                     area: []
68875                 };
68876
68877                 var labelled = {
68878                     point: [],
68879                     line: [],
68880                     area: []
68881                 };
68882
68883                 // Try and find a valid label for labellable entities
68884                 for (k = 0; k < labelable.length; k++) {
68885                     var fontSize = labelStack[k][3];
68886
68887                     for (i = 0; i < labelable[k].length; i++) {
68888                         entity = labelable[k][i];
68889                         geometry = entity.geometry(graph);
68890
68891                         var getName = (geometry === 'line') ? utilDisplayNameForPath : utilDisplayName;
68892                         var name = getName(entity);
68893                         var width = name && textWidth(name, fontSize);
68894                         var p = null;
68895
68896                         if (geometry === 'point' || geometry === 'vertex') {
68897                             // no point or vertex labels in wireframe mode
68898                             // no vertex labels at low zooms (vertices have no icons)
68899                             if (wireframe) continue;
68900                             var renderAs = renderNodeAs[entity.id];
68901                             if (renderAs === 'vertex' && zoom < 17) continue;
68902
68903                             p = getPointLabel(entity, width, fontSize, renderAs);
68904
68905                         } else if (geometry === 'line') {
68906                             p = getLineLabel(entity, width, fontSize);
68907
68908                         } else if (geometry === 'area') {
68909                             p = getAreaLabel(entity, width, fontSize);
68910                         }
68911
68912                         if (p) {
68913                             if (geometry === 'vertex') { geometry = 'point'; }  // treat vertex like point
68914                             p.classes = geometry + ' tag-' + labelStack[k][1];
68915                             positions[geometry].push(p);
68916                             labelled[geometry].push(entity);
68917                         }
68918                     }
68919                 }
68920
68921
68922                 function isInterestingVertex(entity) {
68923                     var selectedIDs = context.selectedIDs();
68924
68925                     return entity.hasInterestingTags() ||
68926                         entity.isEndpoint(graph) ||
68927                         entity.isConnected(graph) ||
68928                         selectedIDs.indexOf(entity.id) !== -1 ||
68929                         graph.parentWays(entity).some(function(parent) {
68930                             return selectedIDs.indexOf(parent.id) !== -1;
68931                         });
68932                 }
68933
68934
68935                 function getPointLabel(entity, width, height, geometry) {
68936                     var y = (geometry === 'point' ? -12 : 0);
68937                     var pointOffsets = {
68938                         ltr: [15, y, 'start'],
68939                         rtl: [-15, y, 'end']
68940                     };
68941
68942                     var textDirection = _mainLocalizer.textDirection();
68943
68944                     var coord = projection(entity.loc);
68945                     var textPadding = 2;
68946                     var offset = pointOffsets[textDirection];
68947                     var p = {
68948                         height: height,
68949                         width: width,
68950                         x: coord[0] + offset[0],
68951                         y: coord[1] + offset[1],
68952                         textAnchor: offset[2]
68953                     };
68954
68955                     // insert a collision box for the text label..
68956                     var bbox;
68957                     if (textDirection === 'rtl') {
68958                         bbox = {
68959                             minX: p.x - width - textPadding,
68960                             minY: p.y - (height / 2) - textPadding,
68961                             maxX: p.x + textPadding,
68962                             maxY: p.y + (height / 2) + textPadding
68963                         };
68964                     } else {
68965                         bbox = {
68966                             minX: p.x - textPadding,
68967                             minY: p.y - (height / 2) - textPadding,
68968                             maxX: p.x + width + textPadding,
68969                             maxY: p.y + (height / 2) + textPadding
68970                         };
68971                     }
68972
68973                     if (tryInsert([bbox], entity.id, true)) {
68974                         return p;
68975                     }
68976                 }
68977
68978
68979                 function getLineLabel(entity, width, height) {
68980                     var viewport = geoExtent(context.projection.clipExtent()).polygon();
68981                     var points = graph.childNodes(entity)
68982                         .map(function(node) { return projection(node.loc); });
68983                     var length = geoPathLength(points);
68984
68985                     if (length < width + 20) return;
68986
68987                     // % along the line to attempt to place the label
68988                     var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70,
68989                                        25, 75, 20, 80, 15, 95, 10, 90, 5, 95];
68990                     var padding = 3;
68991
68992                     for (var i = 0; i < lineOffsets.length; i++) {
68993                         var offset = lineOffsets[i];
68994                         var middle = offset / 100 * length;
68995                         var start = middle - width / 2;
68996
68997                         if (start < 0 || start + width > length) continue;
68998
68999                         // generate subpath and ignore paths that are invalid or don't cross viewport.
69000                         var sub = subpath(points, start, start + width);
69001                         if (!sub || !geoPolygonIntersectsPolygon(viewport, sub, true)) {
69002                             continue;
69003                         }
69004
69005                         var isReverse = reverse(sub);
69006                         if (isReverse) {
69007                             sub = sub.reverse();
69008                         }
69009
69010                         var bboxes = [];
69011                         var boxsize = (height + 2) / 2;
69012
69013                         for (var j = 0; j < sub.length - 1; j++) {
69014                             var a = sub[j];
69015                             var b = sub[j + 1];
69016
69017                             // split up the text into small collision boxes
69018                             var num = Math.max(1, Math.floor(geoVecLength(a, b) / boxsize / 2));
69019
69020                             for (var box = 0; box < num; box++) {
69021                                 var p = geoVecInterp(a, b, box / num);
69022                                 var x0 = p[0] - boxsize - padding;
69023                                 var y0 = p[1] - boxsize - padding;
69024                                 var x1 = p[0] + boxsize + padding;
69025                                 var y1 = p[1] + boxsize + padding;
69026
69027                                 bboxes.push({
69028                                     minX: Math.min(x0, x1),
69029                                     minY: Math.min(y0, y1),
69030                                     maxX: Math.max(x0, x1),
69031                                     maxY: Math.max(y0, y1)
69032                                 });
69033                             }
69034                         }
69035
69036                         if (tryInsert(bboxes, entity.id, false)) {   // accept this one
69037                             return {
69038                                 'font-size': height + 2,
69039                                 lineString: lineString(sub),
69040                                 startOffset: offset + '%'
69041                             };
69042                         }
69043                     }
69044
69045                     function reverse(p) {
69046                         var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
69047                         return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2);
69048                     }
69049
69050                     function lineString(points) {
69051                         return 'M' + points.join('L');
69052                     }
69053
69054                     function subpath(points, from, to) {
69055                         var sofar = 0;
69056                         var start, end, i0, i1;
69057
69058                         for (var i = 0; i < points.length - 1; i++) {
69059                             var a = points[i];
69060                             var b = points[i + 1];
69061                             var current = geoVecLength(a, b);
69062                             var portion;
69063                             if (!start && sofar + current >= from) {
69064                                 portion = (from - sofar) / current;
69065                                 start = [
69066                                     a[0] + portion * (b[0] - a[0]),
69067                                     a[1] + portion * (b[1] - a[1])
69068                                 ];
69069                                 i0 = i + 1;
69070                             }
69071                             if (!end && sofar + current >= to) {
69072                                 portion = (to - sofar) / current;
69073                                 end = [
69074                                     a[0] + portion * (b[0] - a[0]),
69075                                     a[1] + portion * (b[1] - a[1])
69076                                 ];
69077                                 i1 = i + 1;
69078                             }
69079                             sofar += current;
69080                         }
69081
69082                         var result = points.slice(i0, i1);
69083                         result.unshift(start);
69084                         result.push(end);
69085                         return result;
69086                     }
69087                 }
69088
69089
69090                 function getAreaLabel(entity, width, height) {
69091                     var centroid = path.centroid(entity.asGeoJSON(graph, true));
69092                     var extent = entity.extent(graph);
69093                     var areaWidth = projection(extent[1])[0] - projection(extent[0])[0];
69094
69095                     if (isNaN(centroid[0]) || areaWidth < 20) return;
69096
69097                     var preset = _mainPresetIndex.match(entity, context.graph());
69098                     var picon = preset && preset.icon;
69099                     var iconSize = 17;
69100                     var padding = 2;
69101                     var p = {};
69102
69103                     if (picon) {  // icon and label..
69104                         if (addIcon()) {
69105                             addLabel(iconSize + padding);
69106                             return p;
69107                         }
69108                     } else {   // label only..
69109                         if (addLabel(0)) {
69110                             return p;
69111                         }
69112                     }
69113
69114
69115                     function addIcon() {
69116                         var iconX = centroid[0] - (iconSize / 2);
69117                         var iconY = centroid[1] - (iconSize / 2);
69118                         var bbox = {
69119                             minX: iconX,
69120                             minY: iconY,
69121                             maxX: iconX + iconSize,
69122                             maxY: iconY + iconSize
69123                         };
69124
69125                         if (tryInsert([bbox], entity.id + 'I', true)) {
69126                             p.transform = 'translate(' + iconX + ',' + iconY + ')';
69127                             return true;
69128                         }
69129                         return false;
69130                     }
69131
69132                     function addLabel(yOffset) {
69133                         if (width && areaWidth >= width + 20) {
69134                             var labelX = centroid[0];
69135                             var labelY = centroid[1] + yOffset;
69136                             var bbox = {
69137                                 minX: labelX - (width / 2) - padding,
69138                                 minY: labelY - (height / 2) - padding,
69139                                 maxX: labelX + (width / 2) + padding,
69140                                 maxY: labelY + (height / 2) + padding
69141                             };
69142
69143                             if (tryInsert([bbox], entity.id, true)) {
69144                                 p.x = labelX;
69145                                 p.y = labelY;
69146                                 p.textAnchor = 'middle';
69147                                 p.height = height;
69148                                 return true;
69149                             }
69150                         }
69151                         return false;
69152                     }
69153                 }
69154
69155
69156                 // force insert a singular bounding box
69157                 // singular box only, no array, id better be unique
69158                 function doInsert(bbox, id) {
69159                     bbox.id = id;
69160
69161                     var oldbox = _entitybboxes[id];
69162                     if (oldbox) {
69163                         _rdrawn.remove(oldbox);
69164                     }
69165                     _entitybboxes[id] = bbox;
69166                     _rdrawn.insert(bbox);
69167                 }
69168
69169
69170                 function tryInsert(bboxes, id, saveSkipped) {
69171                     var skipped = false;
69172
69173                     for (var i = 0; i < bboxes.length; i++) {
69174                         var bbox = bboxes[i];
69175                         bbox.id = id;
69176
69177                         // Check that label is visible
69178                         if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) {
69179                             skipped = true;
69180                             break;
69181                         }
69182                         if (_rdrawn.collides(bbox)) {
69183                             skipped = true;
69184                             break;
69185                         }
69186                     }
69187
69188                     _entitybboxes[id] = bboxes;
69189
69190                     if (skipped) {
69191                         if (saveSkipped) {
69192                             _rskipped.load(bboxes);
69193                         }
69194                     } else {
69195                         _rdrawn.load(bboxes);
69196                     }
69197
69198                     return !skipped;
69199                 }
69200
69201
69202                 var layer = selection.selectAll('.layer-osm.labels');
69203                 layer.selectAll('.labels-group')
69204                     .data(['halo', 'label', 'debug'])
69205                     .enter()
69206                     .append('g')
69207                     .attr('class', function(d) { return 'labels-group ' + d; });
69208
69209                 var halo = layer.selectAll('.labels-group.halo');
69210                 var label = layer.selectAll('.labels-group.label');
69211                 var debug = layer.selectAll('.labels-group.debug');
69212
69213                 // points
69214                 drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
69215                 drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point);
69216
69217                 // lines
69218                 drawLinePaths(layer, labelled.line, filter, '', positions.line);
69219                 drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
69220                 drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line);
69221
69222                 // areas
69223                 drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
69224                 drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
69225                 drawAreaIcons(label, labelled.area, filter, 'areaicon', positions.area);
69226                 drawAreaIcons(halo, labelled.area, filter, 'areaicon-halo', positions.area);
69227
69228                 // debug
69229                 drawCollisionBoxes(debug, _rskipped, 'debug-skipped');
69230                 drawCollisionBoxes(debug, _rdrawn, 'debug-drawn');
69231
69232                 layer.call(filterLabels);
69233             }
69234
69235
69236             function filterLabels(selection) {
69237                 var drawLayer = selection.selectAll('.layer-osm.labels');
69238                 var layers = drawLayer.selectAll('.labels-group.halo, .labels-group.label');
69239
69240                 layers.selectAll('.nolabel')
69241                     .classed('nolabel', false);
69242
69243                 var mouse = context.map().mouse();
69244                 var graph = context.graph();
69245                 var selectedIDs = context.selectedIDs();
69246                 var ids = [];
69247                 var pad, bbox;
69248
69249                 // hide labels near the mouse
69250                 if (mouse) {
69251                     pad = 20;
69252                     bbox = { minX: mouse[0] - pad, minY: mouse[1] - pad, maxX: mouse[0] + pad, maxY: mouse[1] + pad };
69253                     var nearMouse = _rdrawn.search(bbox).map(function(entity) { return entity.id; });
69254                     ids.push.apply(ids, nearMouse);
69255                 }
69256
69257                 // hide labels on selected nodes (they look weird when dragging / haloed)
69258                 for (var i = 0; i < selectedIDs.length; i++) {
69259                     var entity = graph.hasEntity(selectedIDs[i]);
69260                     if (entity && entity.type === 'node') {
69261                         ids.push(selectedIDs[i]);
69262                     }
69263                 }
69264
69265                 layers.selectAll(utilEntitySelector(ids))
69266                     .classed('nolabel', true);
69267
69268
69269                 // draw the mouse bbox if debugging is on..
69270                 var debug = selection.selectAll('.labels-group.debug');
69271                 var gj = [];
69272                 if (context.getDebug('collision')) {
69273                     gj = bbox ? [{
69274                         type: 'Polygon',
69275                         coordinates: [[
69276                             [bbox.minX, bbox.minY],
69277                             [bbox.maxX, bbox.minY],
69278                             [bbox.maxX, bbox.maxY],
69279                             [bbox.minX, bbox.maxY],
69280                             [bbox.minX, bbox.minY]
69281                         ]]
69282                     }] : [];
69283                 }
69284
69285                 var box = debug.selectAll('.debug-mouse')
69286                     .data(gj);
69287
69288                 // exit
69289                 box.exit()
69290                     .remove();
69291
69292                 // enter/update
69293                 box.enter()
69294                     .append('path')
69295                     .attr('class', 'debug debug-mouse yellow')
69296                     .merge(box)
69297                     .attr('d', d3_geoPath());
69298             }
69299
69300
69301             var throttleFilterLabels = throttle(filterLabels, 100);
69302
69303
69304             drawLabels.observe = function(selection) {
69305                 var listener = function() { throttleFilterLabels(selection); };
69306                 selection.on('mousemove.hidelabels', listener);
69307                 context.on('enter.hidelabels', listener);
69308             };
69309
69310
69311             drawLabels.off = function(selection) {
69312                 throttleFilterLabels.cancel();
69313                 selection.on('mousemove.hidelabels', null);
69314                 context.on('enter.hidelabels', null);
69315             };
69316
69317
69318             return drawLabels;
69319         }
69320
69321         let _layerEnabled$1 = false;
69322         let _qaService$1;
69323
69324         function svgImproveOSM(projection, context, dispatch) {
69325           const throttledRedraw = throttle(() => dispatch.call('change'), 1000);
69326           const minZoom = 12;
69327
69328           let touchLayer = select(null);
69329           let drawLayer = select(null);
69330           let layerVisible = false;
69331
69332           function markerPath(selection, klass) {
69333             selection
69334               .attr('class', klass)
69335               .attr('transform', 'translate(-10, -28)')
69336               .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');
69337           }
69338
69339           // Loosely-coupled improveOSM service for fetching issues
69340           function getService() {
69341             if (services.improveOSM && !_qaService$1) {
69342               _qaService$1 = services.improveOSM;
69343               _qaService$1.on('loaded', throttledRedraw);
69344             } else if (!services.improveOSM && _qaService$1) {
69345               _qaService$1 = null;
69346             }
69347
69348             return _qaService$1;
69349           }
69350
69351           // Show the markers
69352           function editOn() {
69353             if (!layerVisible) {
69354               layerVisible = true;
69355               drawLayer
69356                 .style('display', 'block');
69357             }
69358           }
69359
69360           // Immediately remove the markers and their touch targets
69361           function editOff() {
69362             if (layerVisible) {
69363               layerVisible = false;
69364               drawLayer
69365                 .style('display', 'none');
69366               drawLayer.selectAll('.qaItem.improveOSM')
69367                 .remove();
69368               touchLayer.selectAll('.qaItem.improveOSM')
69369                 .remove();
69370             }
69371           }
69372
69373           // Enable the layer.  This shows the markers and transitions them to visible.
69374           function layerOn() {
69375             editOn();
69376
69377             drawLayer
69378               .style('opacity', 0)
69379               .transition()
69380               .duration(250)
69381               .style('opacity', 1)
69382               .on('end interrupt', () => dispatch.call('change'));
69383           }
69384
69385           // Disable the layer.  This transitions the layer invisible and then hides the markers.
69386           function layerOff() {
69387             throttledRedraw.cancel();
69388             drawLayer.interrupt();
69389             touchLayer.selectAll('.qaItem.improveOSM')
69390               .remove();
69391
69392             drawLayer
69393               .transition()
69394               .duration(250)
69395               .style('opacity', 0)
69396               .on('end interrupt', () => {
69397                 editOff();
69398                 dispatch.call('change');
69399               });
69400           }
69401
69402           // Update the issue markers
69403           function updateMarkers() {
69404             if (!layerVisible || !_layerEnabled$1) return;
69405
69406             const service = getService();
69407             const selectedID = context.selectedErrorID();
69408             const data = (service ? service.getItems(projection) : []);
69409             const getTransform = svgPointTransform(projection);
69410
69411             // Draw markers..
69412             const markers = drawLayer.selectAll('.qaItem.improveOSM')
69413               .data(data, d => d.id);
69414
69415             // exit
69416             markers.exit()
69417               .remove();
69418
69419             // enter
69420             const markersEnter = markers.enter()
69421               .append('g')
69422                 .attr('class', d => `qaItem ${d.service} itemId-${d.id} itemType-${d.itemType}`);
69423
69424             markersEnter
69425               .append('polygon')
69426                 .call(markerPath, 'shadow');
69427
69428             markersEnter
69429               .append('ellipse')
69430                 .attr('cx', 0)
69431                 .attr('cy', 0)
69432                 .attr('rx', 4.5)
69433                 .attr('ry', 2)
69434                 .attr('class', 'stroke');
69435
69436             markersEnter
69437               .append('polygon')
69438                 .attr('fill', 'currentColor')
69439                 .call(markerPath, 'qaItem-fill');
69440
69441             markersEnter
69442               .append('use')
69443                 .attr('transform', 'translate(-6.5, -23)')
69444                 .attr('class', 'icon-annotation')
69445                 .attr('width', '13px')
69446                 .attr('height', '13px')
69447                 .attr('xlink:href', d => {
69448                   const picon = d.icon;
69449
69450                   if (!picon) {
69451                   return '';
69452                   } else {
69453                   const isMaki = /^maki-/.test(picon);
69454                   return `#${picon}${isMaki ? '-11' : ''}`;
69455                   }
69456                 });
69457
69458             // update
69459             markers
69460               .merge(markersEnter)
69461               .sort(sortY)
69462                 .classed('selected', d => d.id === selectedID)
69463                 .attr('transform', getTransform);
69464
69465
69466             // Draw targets..
69467             if (touchLayer.empty()) return;
69468             const fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
69469
69470             const targets = touchLayer.selectAll('.qaItem.improveOSM')
69471               .data(data, d => d.id);
69472
69473             // exit
69474             targets.exit()
69475               .remove();
69476
69477             // enter/update
69478             targets.enter()
69479               .append('rect')
69480                 .attr('width', '20px')
69481                 .attr('height', '30px')
69482                 .attr('x', '-10px')
69483                 .attr('y', '-28px')
69484               .merge(targets)
69485               .sort(sortY)
69486                 .attr('class', d => `qaItem ${d.service} target ${fillClass} itemId-${d.id}`)
69487                 .attr('transform', getTransform);
69488
69489             function sortY(a, b) {
69490               return (a.id === selectedID) ? 1
69491                 : (b.id === selectedID) ? -1
69492                 : b.loc[1] - a.loc[1];
69493             }
69494           }
69495
69496           // Draw the ImproveOSM layer and schedule loading issues and updating markers.
69497           function drawImproveOSM(selection) {
69498             const service = getService();
69499
69500             const surface = context.surface();
69501             if (surface && !surface.empty()) {
69502               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
69503             }
69504
69505             drawLayer = selection.selectAll('.layer-improveOSM')
69506               .data(service ? [0] : []);
69507
69508             drawLayer.exit()
69509               .remove();
69510
69511             drawLayer = drawLayer.enter()
69512               .append('g')
69513                 .attr('class', 'layer-improveOSM')
69514                 .style('display', _layerEnabled$1 ? 'block' : 'none')
69515               .merge(drawLayer);
69516
69517             if (_layerEnabled$1) {
69518               if (service && ~~context.map().zoom() >= minZoom) {
69519                 editOn();
69520                 service.loadIssues(projection);
69521                 updateMarkers();
69522               } else {
69523                 editOff();
69524               }
69525             }
69526           }
69527
69528           // Toggles the layer on and off
69529           drawImproveOSM.enabled = function(val) {
69530             if (!arguments.length) return _layerEnabled$1;
69531
69532             _layerEnabled$1 = val;
69533             if (_layerEnabled$1) {
69534               layerOn();
69535             } else {
69536               layerOff();
69537               if (context.selectedErrorID()) {
69538                 context.enter(modeBrowse(context));
69539               }
69540             }
69541
69542             dispatch.call('change');
69543             return this;
69544           };
69545
69546           drawImproveOSM.supported = () => !!getService();
69547
69548           return drawImproveOSM;
69549         }
69550
69551         let _layerEnabled$2 = false;
69552         let _qaService$2;
69553
69554         function svgOsmose(projection, context, dispatch) {
69555           const throttledRedraw = throttle(() => dispatch.call('change'), 1000);
69556           const minZoom = 12;
69557
69558           let touchLayer = select(null);
69559           let drawLayer = select(null);
69560           let layerVisible = false;
69561
69562           function markerPath(selection, klass) {
69563             selection
69564               .attr('class', klass)
69565               .attr('transform', 'translate(-10, -28)')
69566               .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');
69567           }
69568
69569           // Loosely-coupled osmose service for fetching issues
69570           function getService() {
69571             if (services.osmose && !_qaService$2) {
69572               _qaService$2 = services.osmose;
69573               _qaService$2.on('loaded', throttledRedraw);
69574             } else if (!services.osmose && _qaService$2) {
69575               _qaService$2 = null;
69576             }
69577
69578             return _qaService$2;
69579           }
69580
69581           // Show the markers
69582           function editOn() {
69583             if (!layerVisible) {
69584               layerVisible = true;
69585               drawLayer
69586                 .style('display', 'block');
69587             }
69588           }
69589
69590           // Immediately remove the markers and their touch targets
69591           function editOff() {
69592             if (layerVisible) {
69593               layerVisible = false;
69594               drawLayer
69595                 .style('display', 'none');
69596               drawLayer.selectAll('.qaItem.osmose')
69597                 .remove();
69598               touchLayer.selectAll('.qaItem.osmose')
69599                 .remove();
69600             }
69601           }
69602
69603           // Enable the layer.  This shows the markers and transitions them to visible.
69604           function layerOn() {
69605             editOn();
69606
69607             drawLayer
69608               .style('opacity', 0)
69609               .transition()
69610               .duration(250)
69611               .style('opacity', 1)
69612               .on('end interrupt', () => dispatch.call('change'));
69613           }
69614
69615           // Disable the layer.  This transitions the layer invisible and then hides the markers.
69616           function layerOff() {
69617             throttledRedraw.cancel();
69618             drawLayer.interrupt();
69619             touchLayer.selectAll('.qaItem.osmose')
69620               .remove();
69621
69622             drawLayer
69623               .transition()
69624               .duration(250)
69625               .style('opacity', 0)
69626               .on('end interrupt', () => {
69627                 editOff();
69628                 dispatch.call('change');
69629               });
69630           }
69631
69632           // Update the issue markers
69633           function updateMarkers() {
69634             if (!layerVisible || !_layerEnabled$2) return;
69635
69636             const service = getService();
69637             const selectedID = context.selectedErrorID();
69638             const data = (service ? service.getItems(projection) : []);
69639             const getTransform = svgPointTransform(projection);
69640
69641             // Draw markers..
69642             const markers = drawLayer.selectAll('.qaItem.osmose')
69643               .data(data, d => d.id);
69644
69645             // exit
69646             markers.exit()
69647               .remove();
69648
69649             // enter
69650             const markersEnter = markers.enter()
69651               .append('g')
69652                 .attr('class', d => `qaItem ${d.service} itemId-${d.id} itemType-${d.itemType}`);
69653
69654             markersEnter
69655               .append('polygon')
69656                 .call(markerPath, 'shadow');
69657
69658             markersEnter
69659               .append('ellipse')
69660                 .attr('cx', 0)
69661                 .attr('cy', 0)
69662                 .attr('rx', 4.5)
69663                 .attr('ry', 2)
69664                 .attr('class', 'stroke');
69665
69666             markersEnter
69667               .append('polygon')
69668                 .attr('fill', d => service.getColor(d.item))
69669                 .call(markerPath, 'qaItem-fill');
69670
69671             markersEnter
69672               .append('use')
69673                 .attr('transform', 'translate(-6.5, -23)')
69674                 .attr('class', 'icon-annotation')
69675                 .attr('width', '13px')
69676                 .attr('height', '13px')
69677                 .attr('xlink:href', d => {
69678                   const picon = d.icon;
69679
69680                   if (!picon) {
69681                     return '';
69682                   } else {
69683                     const isMaki = /^maki-/.test(picon);
69684                     return `#${picon}${isMaki ? '-11' : ''}`;
69685                   }
69686                 });
69687
69688             // update
69689             markers
69690               .merge(markersEnter)
69691               .sort(sortY)
69692                 .classed('selected', d => d.id === selectedID)
69693                 .attr('transform', getTransform);
69694
69695             // Draw targets..
69696             if (touchLayer.empty()) return;
69697             const fillClass = context.getDebug('target') ? 'pink' : 'nocolor';
69698
69699             const targets = touchLayer.selectAll('.qaItem.osmose')
69700               .data(data, d => d.id);
69701
69702             // exit
69703             targets.exit()
69704               .remove();
69705
69706             // enter/update
69707             targets.enter()
69708               .append('rect')
69709                 .attr('width', '20px')
69710                 .attr('height', '30px')
69711                 .attr('x', '-10px')
69712                 .attr('y', '-28px')
69713               .merge(targets)
69714               .sort(sortY)
69715                 .attr('class', d => `qaItem ${d.service} target ${fillClass} itemId-${d.id}`)
69716                 .attr('transform', getTransform);
69717
69718             function sortY(a, b) {
69719               return (a.id === selectedID) ? 1
69720                 : (b.id === selectedID) ? -1
69721                 : b.loc[1] - a.loc[1];
69722             }
69723           }
69724
69725           // Draw the Osmose layer and schedule loading issues and updating markers.
69726           function drawOsmose(selection) {
69727             const service = getService();
69728
69729             const surface = context.surface();
69730             if (surface && !surface.empty()) {
69731               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
69732             }
69733
69734             drawLayer = selection.selectAll('.layer-osmose')
69735               .data(service ? [0] : []);
69736
69737             drawLayer.exit()
69738               .remove();
69739
69740             drawLayer = drawLayer.enter()
69741               .append('g')
69742                 .attr('class', 'layer-osmose')
69743                 .style('display', _layerEnabled$2 ? 'block' : 'none')
69744               .merge(drawLayer);
69745
69746             if (_layerEnabled$2) {
69747               if (service && ~~context.map().zoom() >= minZoom) {
69748                 editOn();
69749                 service.loadIssues(projection);
69750                 updateMarkers();
69751               } else {
69752                 editOff();
69753               }
69754             }
69755           }
69756
69757           // Toggles the layer on and off
69758           drawOsmose.enabled = function(val) {
69759             if (!arguments.length) return _layerEnabled$2;
69760
69761             _layerEnabled$2 = val;
69762             if (_layerEnabled$2) {
69763               // Strings supplied by Osmose fetched before showing layer for first time
69764               // NOTE: Currently no way to change locale in iD at runtime, would need to re-call this method if that's ever implemented
69765               // Also, If layer is toggled quickly multiple requests are sent
69766               getService().loadStrings()
69767                 .then(layerOn)
69768                 .catch(err => {
69769                   console.log(err); // eslint-disable-line no-console
69770                 });
69771             } else {
69772               layerOff();
69773               if (context.selectedErrorID()) {
69774                 context.enter(modeBrowse(context));
69775               }
69776             }
69777
69778             dispatch.call('change');
69779             return this;
69780           };
69781
69782           drawOsmose.supported = () => !!getService();
69783
69784           return drawOsmose;
69785         }
69786
69787         function svgStreetside(projection, context, dispatch) {
69788             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
69789             var minZoom = 14;
69790             var minMarkerZoom = 16;
69791             var minViewfieldZoom = 18;
69792             var layer = select(null);
69793             var _viewerYaw = 0;
69794             var _selectedSequence = null;
69795             var _streetside;
69796
69797             /**
69798              * init().
69799              */
69800             function init() {
69801                 if (svgStreetside.initialized) return;  // run once
69802                 svgStreetside.enabled = false;
69803                 svgStreetside.initialized = true;
69804             }
69805
69806             /**
69807              * getService().
69808              */
69809             function getService() {
69810                 if (services.streetside && !_streetside) {
69811                     _streetside = services.streetside;
69812                     _streetside.event
69813                         .on('viewerChanged.svgStreetside', viewerChanged)
69814                         .on('loadedBubbles.svgStreetside', throttledRedraw);
69815                 } else if (!services.streetside && _streetside) {
69816                     _streetside = null;
69817                 }
69818
69819                 return _streetside;
69820             }
69821
69822             /**
69823              * showLayer().
69824              */
69825             function showLayer() {
69826                 var service = getService();
69827                 if (!service) return;
69828
69829                 editOn();
69830
69831                 layer
69832                     .style('opacity', 0)
69833                     .transition()
69834                     .duration(250)
69835                     .style('opacity', 1)
69836                     .on('end', function () { dispatch.call('change'); });
69837             }
69838
69839             /**
69840              * hideLayer().
69841              */
69842             function hideLayer() {
69843                 throttledRedraw.cancel();
69844
69845                 layer
69846                     .transition()
69847                     .duration(250)
69848                     .style('opacity', 0)
69849                     .on('end', editOff);
69850             }
69851
69852             /**
69853              * editOn().
69854              */
69855             function editOn() {
69856                 layer.style('display', 'block');
69857             }
69858
69859             /**
69860              * editOff().
69861              */
69862             function editOff() {
69863                 layer.selectAll('.viewfield-group').remove();
69864                 layer.style('display', 'none');
69865             }
69866
69867             /**
69868              * click() Handles 'bubble' point click event.
69869              */
69870             function click(d) {
69871                 var service = getService();
69872                 if (!service) return;
69873
69874                 // try to preserve the viewer rotation when staying on the same sequence
69875                 if (d.sequenceKey !== _selectedSequence) {
69876                     _viewerYaw = 0;  // reset
69877                 }
69878                 _selectedSequence = d.sequenceKey;
69879
69880                 service
69881                     .selectImage(context, d)
69882                     .then(response => {
69883                         if (response.status === 'ok'){
69884                             service.showViewer(context, _viewerYaw);
69885                         }
69886                     });
69887
69888
69889                 context.map().centerEase(d.loc);
69890             }
69891
69892             /**
69893              * mouseover().
69894              */
69895             function mouseover(d) {
69896                 var service = getService();
69897                 if (service) service.setStyles(context, d);
69898             }
69899
69900             /**
69901              * mouseout().
69902              */
69903             function mouseout() {
69904                 var service = getService();
69905                 if (service) service.setStyles(context, null);
69906             }
69907
69908             /**
69909              * transform().
69910              */
69911             function transform(d) {
69912                 var t = svgPointTransform(projection)(d);
69913                 var rot = d.ca + _viewerYaw;
69914                 if (rot) {
69915                     t += ' rotate(' + Math.floor(rot) + ',0,0)';
69916                 }
69917                 return t;
69918             }
69919
69920
69921             function viewerChanged() {
69922                 var service = getService();
69923                 if (!service) return;
69924
69925                 var viewer = service.viewer();
69926                 if (!viewer) return;
69927
69928                 // update viewfield rotation
69929                 _viewerYaw = viewer.getYaw();
69930
69931                 // avoid updating if the map is currently transformed
69932                 // e.g. during drags or easing.
69933                 if (context.map().isTransformed()) return;
69934
69935                 layer.selectAll('.viewfield-group.currentView')
69936                     .attr('transform', transform);
69937             }
69938
69939
69940             context.photos().on('change.streetside', update);
69941
69942             /**
69943              * update().
69944              */
69945             function update() {
69946                 var viewer = context.container().select('.photoviewer');
69947                 var selected = viewer.empty() ? undefined : viewer.datum();
69948                 var z = ~~context.map().zoom();
69949                 var showMarkers = (z >= minMarkerZoom);
69950                 var showViewfields = (z >= minViewfieldZoom);
69951                 var service = getService();
69952
69953                 var sequences = [];
69954                 var bubbles = [];
69955
69956                 if (context.photos().showsPanoramic()) {
69957                     sequences = (service ? service.sequences(projection) : []);
69958                     bubbles = (service && showMarkers ? service.bubbles(projection) : []);
69959                 }
69960
69961                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
69962                     .data(sequences, function(d) { return d.properties.key; });
69963
69964                 // exit
69965                 traces.exit()
69966                     .remove();
69967
69968                 // enter/update
69969                 traces = traces.enter()
69970                     .append('path')
69971                     .attr('class', 'sequence')
69972                     .merge(traces)
69973                     .attr('d', svgPath(projection).geojson);
69974
69975
69976                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
69977                     .data(bubbles, function(d) {
69978                         // force reenter once bubbles are attached to a sequence
69979                         return d.key + (d.sequenceKey ? 'v1' : 'v0');
69980                     });
69981
69982                 // exit
69983                 groups.exit()
69984                     .remove();
69985
69986                 // enter
69987                 var groupsEnter = groups.enter()
69988                     .append('g')
69989                     .attr('class', 'viewfield-group')
69990                     .on('mouseenter', mouseover)
69991                     .on('mouseleave', mouseout)
69992                     .on('click', click);
69993
69994                 groupsEnter
69995                     .append('g')
69996                     .attr('class', 'viewfield-scale');
69997
69998                 // update
69999                 var markers = groups
70000                     .merge(groupsEnter)
70001                     .sort(function(a, b) {
70002                         return (a === selected) ? 1
70003                             : (b === selected) ? -1
70004                             : b.loc[1] - a.loc[1];
70005                     })
70006                     .attr('transform', transform)
70007                     .select('.viewfield-scale');
70008
70009
70010                 markers.selectAll('circle')
70011                     .data([0])
70012                     .enter()
70013                     .append('circle')
70014                     .attr('dx', '0')
70015                     .attr('dy', '0')
70016                     .attr('r', '6');
70017
70018                 var viewfields = markers.selectAll('.viewfield')
70019                     .data(showViewfields ? [0] : []);
70020
70021                 viewfields.exit()
70022                     .remove();
70023
70024                 // viewfields may or may not be drawn...
70025                 // but if they are, draw below the circles
70026                 viewfields.enter()
70027                     .insert('path', 'circle')
70028                     .attr('class', 'viewfield')
70029                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
70030                     .attr('d', viewfieldPath);
70031
70032                 function viewfieldPath() {
70033                     var d = this.parentNode.__data__;
70034                     if (d.pano) {
70035                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
70036                     } else {
70037                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
70038                     }
70039                 }
70040
70041             }
70042
70043             /**
70044              * drawImages()
70045              * drawImages is the method that is returned (and that runs) everytime 'svgStreetside()' is called.
70046              * 'svgStreetside()' is called from index.js
70047              */
70048             function drawImages(selection) {
70049                 var enabled = svgStreetside.enabled;
70050                 var service = getService();
70051
70052                 layer = selection.selectAll('.layer-streetside-images')
70053                     .data(service ? [0] : []);
70054
70055                 layer.exit()
70056                     .remove();
70057
70058                 var layerEnter = layer.enter()
70059                     .append('g')
70060                     .attr('class', 'layer-streetside-images')
70061                     .style('display', enabled ? 'block' : 'none');
70062
70063                 layerEnter
70064                     .append('g')
70065                     .attr('class', 'sequences');
70066
70067                 layerEnter
70068                     .append('g')
70069                     .attr('class', 'markers');
70070
70071                 layer = layerEnter
70072                     .merge(layer);
70073
70074                 if (enabled) {
70075                     if (service && ~~context.map().zoom() >= minZoom) {
70076                         editOn();
70077                         update();
70078                         service.loadBubbles(projection);
70079                     } else {
70080                         editOff();
70081                     }
70082                 }
70083             }
70084
70085
70086             /**
70087              * drawImages.enabled().
70088              */
70089             drawImages.enabled = function(_) {
70090                 if (!arguments.length) return svgStreetside.enabled;
70091                 svgStreetside.enabled = _;
70092                 if (svgStreetside.enabled) {
70093                     showLayer();
70094                 } else {
70095                     hideLayer();
70096                 }
70097                 dispatch.call('change');
70098                 return this;
70099             };
70100
70101             /**
70102              * drawImages.supported().
70103              */
70104             drawImages.supported = function() {
70105                 return !!getService();
70106             };
70107
70108             init();
70109
70110             return drawImages;
70111         }
70112
70113         function svgMapillaryImages(projection, context, dispatch) {
70114             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70115             var minZoom = 12;
70116             var minMarkerZoom = 16;
70117             var minViewfieldZoom = 18;
70118             var layer = select(null);
70119             var _mapillary;
70120             var viewerCompassAngle;
70121
70122
70123             function init() {
70124                 if (svgMapillaryImages.initialized) return;  // run once
70125                 svgMapillaryImages.enabled = false;
70126                 svgMapillaryImages.initialized = true;
70127             }
70128
70129
70130             function getService() {
70131                 if (services.mapillary && !_mapillary) {
70132                     _mapillary = services.mapillary;
70133                     _mapillary.event.on('loadedImages', throttledRedraw);
70134                     _mapillary.event.on('bearingChanged', function(e) {
70135                         viewerCompassAngle = e;
70136
70137                         // avoid updating if the map is currently transformed
70138                         // e.g. during drags or easing.
70139                         if (context.map().isTransformed()) return;
70140
70141                         layer.selectAll('.viewfield-group.currentView')
70142                             .filter(function(d) {
70143                                 return d.pano;
70144                             })
70145                             .attr('transform', transform);
70146                     });
70147                 } else if (!services.mapillary && _mapillary) {
70148                     _mapillary = null;
70149                 }
70150
70151                 return _mapillary;
70152             }
70153
70154
70155             function showLayer() {
70156                 var service = getService();
70157                 if (!service) return;
70158
70159                 editOn();
70160
70161                 layer
70162                     .style('opacity', 0)
70163                     .transition()
70164                     .duration(250)
70165                     .style('opacity', 1)
70166                     .on('end', function () { dispatch.call('change'); });
70167             }
70168
70169
70170             function hideLayer() {
70171                 throttledRedraw.cancel();
70172
70173                 layer
70174                     .transition()
70175                     .duration(250)
70176                     .style('opacity', 0)
70177                     .on('end', editOff);
70178             }
70179
70180
70181             function editOn() {
70182                 layer.style('display', 'block');
70183             }
70184
70185
70186             function editOff() {
70187                 layer.selectAll('.viewfield-group').remove();
70188                 layer.style('display', 'none');
70189             }
70190
70191
70192             function click(d) {
70193                 var service = getService();
70194                 if (!service) return;
70195
70196                 service
70197                     .selectImage(context, d.key)
70198                     .updateViewer(context, d.key)
70199                     .showViewer(context);
70200
70201                 context.map().centerEase(d.loc);
70202             }
70203
70204
70205             function mouseover(d) {
70206                 var service = getService();
70207                 if (service) service.setStyles(context, d);
70208             }
70209
70210
70211             function mouseout() {
70212                 var service = getService();
70213                 if (service) service.setStyles(context, null);
70214             }
70215
70216
70217             function transform(d) {
70218                 var t = svgPointTransform(projection)(d);
70219                 if (d.pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) {
70220                     t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
70221                 } else if (d.ca) {
70222                     t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
70223                 }
70224                 return t;
70225             }
70226
70227             context.photos().on('change.mapillary_images', update);
70228
70229             function filterImages(images) {
70230                 var showsPano = context.photos().showsPanoramic();
70231                 var showsFlat = context.photos().showsFlat();
70232                 if (!showsPano || !showsFlat) {
70233                     images = images.filter(function(image) {
70234                         if (image.pano) return showsPano;
70235                         return showsFlat;
70236                     });
70237                 }
70238                 return images;
70239             }
70240
70241             function filterSequences(sequences, service) {
70242                 var showsPano = context.photos().showsPanoramic();
70243                 var showsFlat = context.photos().showsFlat();
70244                 if (!showsPano || !showsFlat) {
70245                     sequences = sequences.filter(function(sequence) {
70246                         if (sequence.properties.hasOwnProperty('pano')) {
70247                             if (sequence.properties.pano) return showsPano;
70248                             return showsFlat;
70249                         } else {
70250                             // if the sequence doesn't specify pano or not, search its images
70251                             var cProps = sequence.properties.coordinateProperties;
70252                             if (cProps && cProps.image_keys && cProps.image_keys.length > 0) {
70253                                 for (var index in cProps.image_keys) {
70254                                     var imageKey = cProps.image_keys[index];
70255                                     var image = service.cachedImage(imageKey);
70256                                     if (image && image.hasOwnProperty('pano')) {
70257                                         if (image.pano) return showsPano;
70258                                         return showsFlat;
70259                                     }
70260                                 }
70261                             }
70262                         }
70263                     });
70264                 }
70265                 return sequences;
70266             }
70267
70268             function update() {
70269
70270                 var z = ~~context.map().zoom();
70271                 var showMarkers = (z >= minMarkerZoom);
70272                 var showViewfields = (z >= minViewfieldZoom);
70273
70274                 var service = getService();
70275                 var selectedKey = service && service.getSelectedImageKey();
70276                 var sequences = (service ? service.sequences(projection) : []);
70277                 var images = (service && showMarkers ? service.images(projection) : []);
70278
70279                 images = filterImages(images);
70280                 sequences = filterSequences(sequences, service);
70281
70282                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
70283                     .data(sequences, function(d) { return d.properties.key; });
70284
70285                 // exit
70286                 traces.exit()
70287                     .remove();
70288
70289                 // enter/update
70290                 traces = traces.enter()
70291                     .append('path')
70292                     .attr('class', 'sequence')
70293                     .merge(traces)
70294                     .attr('d', svgPath(projection).geojson);
70295
70296
70297                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
70298                     .data(images, function(d) { return d.key; });
70299
70300                 // exit
70301                 groups.exit()
70302                     .remove();
70303
70304                 // enter
70305                 var groupsEnter = groups.enter()
70306                     .append('g')
70307                     .attr('class', 'viewfield-group')
70308                     .on('mouseenter', mouseover)
70309                     .on('mouseleave', mouseout)
70310                     .on('click', click);
70311
70312                 groupsEnter
70313                     .append('g')
70314                     .attr('class', 'viewfield-scale');
70315
70316                 // update
70317                 var markers = groups
70318                     .merge(groupsEnter)
70319                     .sort(function(a, b) {
70320                         return (a.key === selectedKey) ? 1
70321                             : (b.key === selectedKey) ? -1
70322                             : b.loc[1] - a.loc[1];  // sort Y
70323                     })
70324                     .attr('transform', transform)
70325                     .select('.viewfield-scale');
70326
70327
70328                 markers.selectAll('circle')
70329                     .data([0])
70330                     .enter()
70331                     .append('circle')
70332                     .attr('dx', '0')
70333                     .attr('dy', '0')
70334                     .attr('r', '6');
70335
70336                 var viewfields = markers.selectAll('.viewfield')
70337                     .data(showViewfields ? [0] : []);
70338
70339                 viewfields.exit()
70340                     .remove();
70341
70342                 viewfields.enter()               // viewfields may or may not be drawn...
70343                     .insert('path', 'circle')    // but if they are, draw below the circles
70344                     .attr('class', 'viewfield')
70345                     .classed('pano', function() { return this.parentNode.__data__.pano; })
70346                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
70347                     .attr('d', viewfieldPath);
70348
70349                 function viewfieldPath() {
70350                     var d = this.parentNode.__data__;
70351                     if (d.pano) {
70352                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
70353                     } else {
70354                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
70355                     }
70356                 }
70357             }
70358
70359
70360             function drawImages(selection) {
70361                 var enabled = svgMapillaryImages.enabled;
70362                 var service = getService();
70363
70364                 layer = selection.selectAll('.layer-mapillary')
70365                     .data(service ? [0] : []);
70366
70367                 layer.exit()
70368                     .remove();
70369
70370                 var layerEnter = layer.enter()
70371                     .append('g')
70372                     .attr('class', 'layer-mapillary')
70373                     .style('display', enabled ? 'block' : 'none');
70374
70375                 layerEnter
70376                     .append('g')
70377                     .attr('class', 'sequences');
70378
70379                 layerEnter
70380                     .append('g')
70381                     .attr('class', 'markers');
70382
70383                 layer = layerEnter
70384                     .merge(layer);
70385
70386                 if (enabled) {
70387                     if (service && ~~context.map().zoom() >= minZoom) {
70388                         editOn();
70389                         update();
70390                         service.loadImages(projection);
70391                     } else {
70392                         editOff();
70393                     }
70394                 }
70395             }
70396
70397
70398             drawImages.enabled = function(_) {
70399                 if (!arguments.length) return svgMapillaryImages.enabled;
70400                 svgMapillaryImages.enabled = _;
70401                 if (svgMapillaryImages.enabled) {
70402                     showLayer();
70403                 } else {
70404                     hideLayer();
70405                 }
70406                 dispatch.call('change');
70407                 return this;
70408             };
70409
70410
70411             drawImages.supported = function() {
70412                 return !!getService();
70413             };
70414
70415
70416             init();
70417             return drawImages;
70418         }
70419
70420         function svgMapillarySigns(projection, context, dispatch) {
70421             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70422             var minZoom = 12;
70423             var layer = select(null);
70424             var _mapillary;
70425
70426
70427             function init() {
70428                 if (svgMapillarySigns.initialized) return;  // run once
70429                 svgMapillarySigns.enabled = false;
70430                 svgMapillarySigns.initialized = true;
70431             }
70432
70433
70434             function getService() {
70435                 if (services.mapillary && !_mapillary) {
70436                     _mapillary = services.mapillary;
70437                     _mapillary.event.on('loadedSigns', throttledRedraw);
70438                 } else if (!services.mapillary && _mapillary) {
70439                     _mapillary = null;
70440                 }
70441                 return _mapillary;
70442             }
70443
70444
70445             function showLayer() {
70446                 var service = getService();
70447                 if (!service) return;
70448
70449                 editOn();
70450             }
70451
70452
70453             function hideLayer() {
70454                 throttledRedraw.cancel();
70455                 editOff();
70456             }
70457
70458
70459             function editOn() {
70460                 layer.style('display', 'block');
70461             }
70462
70463
70464             function editOff() {
70465                 layer.selectAll('.icon-sign').remove();
70466                 layer.style('display', 'none');
70467             }
70468
70469
70470             function click(d) {
70471                 var service = getService();
70472                 if (!service) return;
70473
70474                 context.map().centerEase(d.loc);
70475
70476                 var selectedImageKey = service.getSelectedImageKey();
70477                 var imageKey;
70478
70479                 // Pick one of the images the sign was detected in,
70480                 // preference given to an image already selected.
70481                 d.detections.forEach(function(detection) {
70482                     if (!imageKey || selectedImageKey === detection.image_key) {
70483                         imageKey = detection.image_key;
70484                     }
70485                 });
70486
70487                 service
70488                     .selectImage(context, imageKey)
70489                     .updateViewer(context, imageKey)
70490                     .showViewer(context);
70491             }
70492
70493
70494             function update() {
70495                 var service = getService();
70496                 var data = (service ? service.signs(projection) : []);
70497                 var selectedImageKey = service.getSelectedImageKey();
70498                 var transform = svgPointTransform(projection);
70499
70500                 var signs = layer.selectAll('.icon-sign')
70501                     .data(data, function(d) { return d.key; });
70502
70503                 // exit
70504                 signs.exit()
70505                     .remove();
70506
70507                 // enter
70508                 var enter = signs.enter()
70509                     .append('g')
70510                     .attr('class', 'icon-sign icon-detected')
70511                     .on('click', click);
70512
70513                 enter
70514                     .append('use')
70515                     .attr('width', '24px')
70516                     .attr('height', '24px')
70517                     .attr('x', '-12px')
70518                     .attr('y', '-12px')
70519                     .attr('xlink:href', function(d) { return '#' + d.value; });
70520
70521                 enter
70522                     .append('rect')
70523                     .attr('width', '24px')
70524                     .attr('height', '24px')
70525                     .attr('x', '-12px')
70526                     .attr('y', '-12px');
70527
70528                 // update
70529                 signs
70530                     .merge(enter)
70531                     .attr('transform', transform)
70532                     .classed('currentView', function(d) {
70533                         return d.detections.some(function(detection) {
70534                             return detection.image_key === selectedImageKey;
70535                         });
70536                     })
70537                     .sort(function(a, b) {
70538                         var aSelected = a.detections.some(function(detection) {
70539                             return detection.image_key === selectedImageKey;
70540                         });
70541                         var bSelected = b.detections.some(function(detection) {
70542                             return detection.image_key === selectedImageKey;
70543                         });
70544                         if (aSelected === bSelected) {
70545                             return b.loc[1] - a.loc[1]; // sort Y
70546                         } else if (aSelected) {
70547                             return 1;
70548                         }
70549                         return -1;
70550                     });
70551             }
70552
70553
70554             function drawSigns(selection) {
70555                 var enabled = svgMapillarySigns.enabled;
70556                 var service = getService();
70557
70558                 layer = selection.selectAll('.layer-mapillary-signs')
70559                     .data(service ? [0] : []);
70560
70561                 layer.exit()
70562                     .remove();
70563
70564                 layer = layer.enter()
70565                     .append('g')
70566                     .attr('class', 'layer-mapillary-signs layer-mapillary-detections')
70567                     .style('display', enabled ? 'block' : 'none')
70568                     .merge(layer);
70569
70570                 if (enabled) {
70571                     if (service && ~~context.map().zoom() >= minZoom) {
70572                         editOn();
70573                         update();
70574                         service.loadSigns(projection);
70575                     } else {
70576                         editOff();
70577                     }
70578                 }
70579             }
70580
70581
70582             drawSigns.enabled = function(_) {
70583                 if (!arguments.length) return svgMapillarySigns.enabled;
70584                 svgMapillarySigns.enabled = _;
70585                 if (svgMapillarySigns.enabled) {
70586                     showLayer();
70587                 } else {
70588                     hideLayer();
70589                 }
70590                 dispatch.call('change');
70591                 return this;
70592             };
70593
70594
70595             drawSigns.supported = function() {
70596                 return !!getService();
70597             };
70598
70599
70600             init();
70601             return drawSigns;
70602         }
70603
70604         function svgMapillaryMapFeatures(projection, context, dispatch) {
70605             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70606             var minZoom = 12;
70607             var layer = select(null);
70608             var _mapillary;
70609
70610
70611             function init() {
70612                 if (svgMapillaryMapFeatures.initialized) return;  // run once
70613                 svgMapillaryMapFeatures.enabled = false;
70614                 svgMapillaryMapFeatures.initialized = true;
70615             }
70616
70617
70618             function getService() {
70619                 if (services.mapillary && !_mapillary) {
70620                     _mapillary = services.mapillary;
70621                     _mapillary.event.on('loadedMapFeatures', throttledRedraw);
70622                 } else if (!services.mapillary && _mapillary) {
70623                     _mapillary = null;
70624                 }
70625                 return _mapillary;
70626             }
70627
70628
70629             function showLayer() {
70630                 var service = getService();
70631                 if (!service) return;
70632
70633                 editOn();
70634             }
70635
70636
70637             function hideLayer() {
70638                 throttledRedraw.cancel();
70639                 editOff();
70640             }
70641
70642
70643             function editOn() {
70644                 layer.style('display', 'block');
70645             }
70646
70647
70648             function editOff() {
70649                 layer.selectAll('.icon-map-feature').remove();
70650                 layer.style('display', 'none');
70651             }
70652
70653
70654             function click(d) {
70655                 var service = getService();
70656                 if (!service) return;
70657
70658                 context.map().centerEase(d.loc);
70659
70660                 var selectedImageKey = service.getSelectedImageKey();
70661                 var imageKey;
70662
70663                 // Pick one of the images the map feature was detected in,
70664                 // preference given to an image already selected.
70665                 d.detections.forEach(function(detection) {
70666                     if (!imageKey || selectedImageKey === detection.image_key) {
70667                         imageKey = detection.image_key;
70668                     }
70669                 });
70670
70671                 service
70672                     .selectImage(context, imageKey)
70673                     .updateViewer(context, imageKey)
70674                     .showViewer(context);
70675             }
70676
70677
70678             function update() {
70679                 var service = getService();
70680                 var data = (service ? service.mapFeatures(projection) : []);
70681                 var selectedImageKey = service && service.getSelectedImageKey();
70682                 var transform = svgPointTransform(projection);
70683
70684                 var mapFeatures = layer.selectAll('.icon-map-feature')
70685                     .data(data, function(d) { return d.key; });
70686
70687                 // exit
70688                 mapFeatures.exit()
70689                     .remove();
70690
70691                 // enter
70692                 var enter = mapFeatures.enter()
70693                     .append('g')
70694                     .attr('class', 'icon-map-feature icon-detected')
70695                     .on('click', click);
70696
70697                 enter
70698                     .append('title')
70699                     .text(function(d) {
70700                         var id = d.value.replace(/--/g, '.').replace(/-/g, '_');
70701                         return _t('mapillary_map_features.' + id);
70702                     });
70703
70704                 enter
70705                     .append('use')
70706                     .attr('width', '24px')
70707                     .attr('height', '24px')
70708                     .attr('x', '-12px')
70709                     .attr('y', '-12px')
70710                     .attr('xlink:href', function(d) {
70711                         if (d.value === 'object--billboard') {
70712                             // no billboard icon right now, so use the advertisement icon
70713                             return '#object--sign--advertisement';
70714                         }
70715                         return '#' + d.value;
70716                     });
70717
70718                 enter
70719                     .append('rect')
70720                     .attr('width', '24px')
70721                     .attr('height', '24px')
70722                     .attr('x', '-12px')
70723                     .attr('y', '-12px');
70724
70725                 // update
70726                 mapFeatures
70727                     .merge(enter)
70728                     .attr('transform', transform)
70729                     .classed('currentView', function(d) {
70730                         return d.detections.some(function(detection) {
70731                             return detection.image_key === selectedImageKey;
70732                         });
70733                     })
70734                     .sort(function(a, b) {
70735                         var aSelected = a.detections.some(function(detection) {
70736                             return detection.image_key === selectedImageKey;
70737                         });
70738                         var bSelected = b.detections.some(function(detection) {
70739                             return detection.image_key === selectedImageKey;
70740                         });
70741                         if (aSelected === bSelected) {
70742                             return b.loc[1] - a.loc[1]; // sort Y
70743                         } else if (aSelected) {
70744                             return 1;
70745                         }
70746                         return -1;
70747                     });
70748             }
70749
70750
70751             function drawMapFeatures(selection) {
70752                 var enabled = svgMapillaryMapFeatures.enabled;
70753                 var service = getService();
70754
70755                 layer = selection.selectAll('.layer-mapillary-map-features')
70756                     .data(service ? [0] : []);
70757
70758                 layer.exit()
70759                     .remove();
70760
70761                 layer = layer.enter()
70762                     .append('g')
70763                     .attr('class', 'layer-mapillary-map-features layer-mapillary-detections')
70764                     .style('display', enabled ? 'block' : 'none')
70765                     .merge(layer);
70766
70767                 if (enabled) {
70768                     if (service && ~~context.map().zoom() >= minZoom) {
70769                         editOn();
70770                         update();
70771                         service.loadMapFeatures(projection);
70772                     } else {
70773                         editOff();
70774                     }
70775                 }
70776             }
70777
70778
70779             drawMapFeatures.enabled = function(_) {
70780                 if (!arguments.length) return svgMapillaryMapFeatures.enabled;
70781                 svgMapillaryMapFeatures.enabled = _;
70782                 if (svgMapillaryMapFeatures.enabled) {
70783                     showLayer();
70784                 } else {
70785                     hideLayer();
70786                 }
70787                 dispatch.call('change');
70788                 return this;
70789             };
70790
70791
70792             drawMapFeatures.supported = function() {
70793                 return !!getService();
70794             };
70795
70796
70797             init();
70798             return drawMapFeatures;
70799         }
70800
70801         function svgOpenstreetcamImages(projection, context, dispatch) {
70802             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70803             var minZoom = 12;
70804             var minMarkerZoom = 16;
70805             var minViewfieldZoom = 18;
70806             var layer = select(null);
70807             var _openstreetcam;
70808
70809
70810             function init() {
70811                 if (svgOpenstreetcamImages.initialized) return;  // run once
70812                 svgOpenstreetcamImages.enabled = false;
70813                 svgOpenstreetcamImages.initialized = true;
70814             }
70815
70816
70817             function getService() {
70818                 if (services.openstreetcam && !_openstreetcam) {
70819                     _openstreetcam = services.openstreetcam;
70820                     _openstreetcam.event.on('loadedImages', throttledRedraw);
70821                 } else if (!services.openstreetcam && _openstreetcam) {
70822                     _openstreetcam = null;
70823                 }
70824
70825                 return _openstreetcam;
70826             }
70827
70828
70829             function showLayer() {
70830                 var service = getService();
70831                 if (!service) return;
70832
70833                 editOn();
70834
70835                 layer
70836                     .style('opacity', 0)
70837                     .transition()
70838                     .duration(250)
70839                     .style('opacity', 1)
70840                     .on('end', function () { dispatch.call('change'); });
70841             }
70842
70843
70844             function hideLayer() {
70845                 throttledRedraw.cancel();
70846
70847                 layer
70848                     .transition()
70849                     .duration(250)
70850                     .style('opacity', 0)
70851                     .on('end', editOff);
70852             }
70853
70854
70855             function editOn() {
70856                 layer.style('display', 'block');
70857             }
70858
70859
70860             function editOff() {
70861                 layer.selectAll('.viewfield-group').remove();
70862                 layer.style('display', 'none');
70863             }
70864
70865
70866             function click(d) {
70867                 var service = getService();
70868                 if (!service) return;
70869
70870                 service
70871                     .selectImage(context, d)
70872                     .updateViewer(context, d)
70873                     .showViewer(context);
70874
70875                 context.map().centerEase(d.loc);
70876             }
70877
70878
70879             function mouseover(d) {
70880                 var service = getService();
70881                 if (service) service.setStyles(context, d);
70882             }
70883
70884
70885             function mouseout() {
70886                 var service = getService();
70887                 if (service) service.setStyles(context, null);
70888             }
70889
70890
70891             function transform(d) {
70892                 var t = svgPointTransform(projection)(d);
70893                 if (d.ca) {
70894                     t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
70895                 }
70896                 return t;
70897             }
70898
70899
70900             context.photos().on('change.openstreetcam_images', update);
70901
70902             function update() {
70903                 var viewer = context.container().select('.photoviewer');
70904                 var selected = viewer.empty() ? undefined : viewer.datum();
70905
70906                 var z = ~~context.map().zoom();
70907                 var showMarkers = (z >= minMarkerZoom);
70908                 var showViewfields = (z >= minViewfieldZoom);
70909
70910                 var service = getService();
70911                 var sequences = [];
70912                 var images = [];
70913
70914                 if (context.photos().showsFlat()) {
70915                     sequences = (service ? service.sequences(projection) : []);
70916                     images = (service && showMarkers ? service.images(projection) : []);
70917                 }
70918
70919                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
70920                     .data(sequences, function(d) { return d.properties.key; });
70921
70922                 // exit
70923                 traces.exit()
70924                     .remove();
70925
70926                 // enter/update
70927                 traces = traces.enter()
70928                     .append('path')
70929                     .attr('class', 'sequence')
70930                     .merge(traces)
70931                     .attr('d', svgPath(projection).geojson);
70932
70933
70934                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
70935                     .data(images, function(d) { return d.key; });
70936
70937                 // exit
70938                 groups.exit()
70939                     .remove();
70940
70941                 // enter
70942                 var groupsEnter = groups.enter()
70943                     .append('g')
70944                     .attr('class', 'viewfield-group')
70945                     .on('mouseenter', mouseover)
70946                     .on('mouseleave', mouseout)
70947                     .on('click', click);
70948
70949                 groupsEnter
70950                     .append('g')
70951                     .attr('class', 'viewfield-scale');
70952
70953                 // update
70954                 var markers = groups
70955                     .merge(groupsEnter)
70956                     .sort(function(a, b) {
70957                         return (a === selected) ? 1
70958                             : (b === selected) ? -1
70959                             : b.loc[1] - a.loc[1];  // sort Y
70960                     })
70961                     .attr('transform', transform)
70962                     .select('.viewfield-scale');
70963
70964
70965                 markers.selectAll('circle')
70966                     .data([0])
70967                     .enter()
70968                     .append('circle')
70969                     .attr('dx', '0')
70970                     .attr('dy', '0')
70971                     .attr('r', '6');
70972
70973                 var viewfields = markers.selectAll('.viewfield')
70974                     .data(showViewfields ? [0] : []);
70975
70976                 viewfields.exit()
70977                     .remove();
70978
70979                 viewfields.enter()               // viewfields may or may not be drawn...
70980                     .insert('path', 'circle')    // but if they are, draw below the circles
70981                     .attr('class', 'viewfield')
70982                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
70983                     .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');
70984             }
70985
70986
70987             function drawImages(selection) {
70988                 var enabled = svgOpenstreetcamImages.enabled,
70989                     service = getService();
70990
70991                 layer = selection.selectAll('.layer-openstreetcam')
70992                     .data(service ? [0] : []);
70993
70994                 layer.exit()
70995                     .remove();
70996
70997                 var layerEnter = layer.enter()
70998                     .append('g')
70999                     .attr('class', 'layer-openstreetcam')
71000                     .style('display', enabled ? 'block' : 'none');
71001
71002                 layerEnter
71003                     .append('g')
71004                     .attr('class', 'sequences');
71005
71006                 layerEnter
71007                     .append('g')
71008                     .attr('class', 'markers');
71009
71010                 layer = layerEnter
71011                     .merge(layer);
71012
71013                 if (enabled) {
71014                     if (service && ~~context.map().zoom() >= minZoom) {
71015                         editOn();
71016                         update();
71017                         service.loadImages(projection);
71018                     } else {
71019                         editOff();
71020                     }
71021                 }
71022             }
71023
71024
71025             drawImages.enabled = function(_) {
71026                 if (!arguments.length) return svgOpenstreetcamImages.enabled;
71027                 svgOpenstreetcamImages.enabled = _;
71028                 if (svgOpenstreetcamImages.enabled) {
71029                     showLayer();
71030                 } else {
71031                     hideLayer();
71032                 }
71033                 dispatch.call('change');
71034                 return this;
71035             };
71036
71037
71038             drawImages.supported = function() {
71039                 return !!getService();
71040             };
71041
71042
71043             init();
71044             return drawImages;
71045         }
71046
71047         function svgOsm(projection, context, dispatch) {
71048             var enabled = true;
71049
71050
71051             function drawOsm(selection) {
71052                 selection.selectAll('.layer-osm')
71053                     .data(['covered', 'areas', 'lines', 'points', 'labels'])
71054                     .enter()
71055                     .append('g')
71056                     .attr('class', function(d) { return 'layer-osm ' + d; });
71057
71058                 selection.selectAll('.layer-osm.points').selectAll('.points-group')
71059                     .data(['points', 'midpoints', 'vertices', 'turns'])
71060                     .enter()
71061                     .append('g')
71062                     .attr('class', function(d) { return 'points-group ' + d; });
71063             }
71064
71065
71066             function showLayer() {
71067                 var layer = context.surface().selectAll('.data-layer.osm');
71068                 layer.interrupt();
71069
71070                 layer
71071                     .classed('disabled', false)
71072                     .style('opacity', 0)
71073                     .transition()
71074                     .duration(250)
71075                     .style('opacity', 1)
71076                     .on('end interrupt', function () {
71077                         dispatch.call('change');
71078                     });
71079             }
71080
71081
71082             function hideLayer() {
71083                 var layer = context.surface().selectAll('.data-layer.osm');
71084                 layer.interrupt();
71085
71086                 layer
71087                     .transition()
71088                     .duration(250)
71089                     .style('opacity', 0)
71090                     .on('end interrupt', function () {
71091                         layer.classed('disabled', true);
71092                         dispatch.call('change');
71093                     });
71094             }
71095
71096
71097             drawOsm.enabled = function(val) {
71098                 if (!arguments.length) return enabled;
71099                 enabled = val;
71100
71101                 if (enabled) {
71102                     showLayer();
71103                 } else {
71104                     hideLayer();
71105                 }
71106
71107                 dispatch.call('change');
71108                 return this;
71109             };
71110
71111
71112             return drawOsm;
71113         }
71114
71115         var _notesEnabled = false;
71116         var _osmService;
71117
71118
71119         function svgNotes(projection, context, dispatch$1) {
71120             if (!dispatch$1) { dispatch$1 = dispatch('change'); }
71121             var throttledRedraw = throttle(function () { dispatch$1.call('change'); }, 1000);
71122             var minZoom = 12;
71123             var touchLayer = select(null);
71124             var drawLayer = select(null);
71125             var _notesVisible = false;
71126
71127
71128             function markerPath(selection, klass) {
71129                 selection
71130                     .attr('class', klass)
71131                     .attr('transform', 'translate(-8, -22)')
71132                     .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');
71133             }
71134
71135
71136             // Loosely-coupled osm service for fetching notes.
71137             function getService() {
71138                 if (services.osm && !_osmService) {
71139                     _osmService = services.osm;
71140                     _osmService.on('loadedNotes', throttledRedraw);
71141                 } else if (!services.osm && _osmService) {
71142                     _osmService = null;
71143                 }
71144
71145                 return _osmService;
71146             }
71147
71148
71149             // Show the notes
71150             function editOn() {
71151                 if (!_notesVisible) {
71152                     _notesVisible = true;
71153                     drawLayer
71154                         .style('display', 'block');
71155                 }
71156             }
71157
71158
71159             // Immediately remove the notes and their touch targets
71160             function editOff() {
71161                 if (_notesVisible) {
71162                     _notesVisible = false;
71163                     drawLayer
71164                         .style('display', 'none');
71165                     drawLayer.selectAll('.note')
71166                         .remove();
71167                     touchLayer.selectAll('.note')
71168                         .remove();
71169                 }
71170             }
71171
71172
71173             // Enable the layer.  This shows the notes and transitions them to visible.
71174             function layerOn() {
71175                 editOn();
71176
71177                 drawLayer
71178                     .style('opacity', 0)
71179                     .transition()
71180                     .duration(250)
71181                     .style('opacity', 1)
71182                     .on('end interrupt', function () {
71183                         dispatch$1.call('change');
71184                     });
71185             }
71186
71187
71188             // Disable the layer.  This transitions the layer invisible and then hides the notes.
71189             function layerOff() {
71190                 throttledRedraw.cancel();
71191                 drawLayer.interrupt();
71192                 touchLayer.selectAll('.note')
71193                     .remove();
71194
71195                 drawLayer
71196                     .transition()
71197                     .duration(250)
71198                     .style('opacity', 0)
71199                     .on('end interrupt', function () {
71200                         editOff();
71201                         dispatch$1.call('change');
71202                     });
71203             }
71204
71205
71206             // Update the note markers
71207             function updateMarkers() {
71208                 if (!_notesVisible || !_notesEnabled) return;
71209
71210                 var service = getService();
71211                 var selectedID = context.selectedNoteID();
71212                 var data = (service ? service.notes(projection) : []);
71213                 var getTransform = svgPointTransform(projection);
71214
71215                 // Draw markers..
71216                 var notes = drawLayer.selectAll('.note')
71217                     .data(data, function(d) { return d.status + d.id; });
71218
71219                 // exit
71220                 notes.exit()
71221                     .remove();
71222
71223                 // enter
71224                 var notesEnter = notes.enter()
71225                     .append('g')
71226                     .attr('class', function(d) { return 'note note-' + d.id + ' ' + d.status; })
71227                     .classed('new', function(d) { return d.id < 0; });
71228
71229                 notesEnter
71230                     .append('ellipse')
71231                     .attr('cx', 0.5)
71232                     .attr('cy', 1)
71233                     .attr('rx', 6.5)
71234                     .attr('ry', 3)
71235                     .attr('class', 'stroke');
71236
71237                 notesEnter
71238                     .append('path')
71239                     .call(markerPath, 'shadow');
71240
71241                 notesEnter
71242                     .append('use')
71243                     .attr('class', 'note-fill')
71244                     .attr('width', '20px')
71245                     .attr('height', '20px')
71246                     .attr('x', '-8px')
71247                     .attr('y', '-22px')
71248                     .attr('xlink:href', '#iD-icon-note');
71249
71250                 notesEnter.selectAll('.icon-annotation')
71251                     .data(function(d) { return [d]; })
71252                     .enter()
71253                     .append('use')
71254                     .attr('class', 'icon-annotation')
71255                     .attr('width', '10px')
71256                     .attr('height', '10px')
71257                     .attr('x', '-3px')
71258                     .attr('y', '-19px')
71259                     .attr('xlink:href', function(d) {
71260                         return '#iD-icon-' + (d.id < 0 ? 'plus' : (d.status === 'open' ? 'close' : 'apply'));
71261                     });
71262
71263                 // update
71264                 notes
71265                     .merge(notesEnter)
71266                     .sort(sortY)
71267                     .classed('selected', function(d) {
71268                         var mode = context.mode();
71269                         var isMoving = mode && mode.id === 'drag-note';  // no shadows when dragging
71270                         return !isMoving && d.id === selectedID;
71271                     })
71272                     .attr('transform', getTransform);
71273
71274
71275                 // Draw targets..
71276                 if (touchLayer.empty()) return;
71277                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71278
71279                 var targets = touchLayer.selectAll('.note')
71280                     .data(data, function(d) { return d.id; });
71281
71282                 // exit
71283                 targets.exit()
71284                     .remove();
71285
71286                 // enter/update
71287                 targets.enter()
71288                     .append('rect')
71289                     .attr('width', '20px')
71290                     .attr('height', '20px')
71291                     .attr('x', '-8px')
71292                     .attr('y', '-22px')
71293                     .merge(targets)
71294                     .sort(sortY)
71295                     .attr('class', function(d) {
71296                         var newClass = (d.id < 0 ? 'new' : '');
71297                         return 'note target note-' + d.id + ' ' + fillClass + newClass;
71298                     })
71299                     .attr('transform', getTransform);
71300
71301
71302                 function sortY(a, b) {
71303                     return (a.id === selectedID) ? 1 : (b.id === selectedID) ? -1 : b.loc[1] - a.loc[1];
71304                 }
71305             }
71306
71307
71308             // Draw the notes layer and schedule loading notes and updating markers.
71309             function drawNotes(selection) {
71310                 var service = getService();
71311
71312                 var surface = context.surface();
71313                 if (surface && !surface.empty()) {
71314                     touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
71315                 }
71316
71317                 drawLayer = selection.selectAll('.layer-notes')
71318                     .data(service ? [0] : []);
71319
71320                 drawLayer.exit()
71321                     .remove();
71322
71323                 drawLayer = drawLayer.enter()
71324                     .append('g')
71325                     .attr('class', 'layer-notes')
71326                     .style('display', _notesEnabled ? 'block' : 'none')
71327                     .merge(drawLayer);
71328
71329                 if (_notesEnabled) {
71330                     if (service && ~~context.map().zoom() >= minZoom) {
71331                         editOn();
71332                         service.loadNotes(projection);
71333                         updateMarkers();
71334                     } else {
71335                         editOff();
71336                     }
71337                 }
71338             }
71339
71340
71341             // Toggles the layer on and off
71342             drawNotes.enabled = function(val) {
71343                 if (!arguments.length) return _notesEnabled;
71344
71345                 _notesEnabled = val;
71346                 if (_notesEnabled) {
71347                     layerOn();
71348                 } else {
71349                     layerOff();
71350                     if (context.selectedNoteID()) {
71351                         context.enter(modeBrowse(context));
71352                     }
71353                 }
71354
71355                 dispatch$1.call('change');
71356                 return this;
71357             };
71358
71359
71360             return drawNotes;
71361         }
71362
71363         function svgTouch() {
71364
71365             function drawTouch(selection) {
71366                 selection.selectAll('.layer-touch')
71367                     .data(['areas', 'lines', 'points', 'turns', 'markers'])
71368                     .enter()
71369                     .append('g')
71370                     .attr('class', function(d) { return 'layer-touch ' + d; });
71371             }
71372
71373             return drawTouch;
71374         }
71375
71376         function refresh(selection, node) {
71377             var cr = node.getBoundingClientRect();
71378             var prop = [cr.width, cr.height];
71379             selection.property('__dimensions__', prop);
71380             return prop;
71381         }
71382
71383         function utilGetDimensions(selection, force) {
71384             if (!selection || selection.empty()) {
71385                 return [0, 0];
71386             }
71387             var node = selection.node(),
71388                 cached = selection.property('__dimensions__');
71389             return (!cached || force) ? refresh(selection, node) : cached;
71390         }
71391
71392
71393         function utilSetDimensions(selection, dimensions) {
71394             if (!selection || selection.empty()) {
71395                 return selection;
71396             }
71397             var node = selection.node();
71398             if (dimensions === null) {
71399                 refresh(selection, node);
71400                 return selection;
71401             }
71402             return selection
71403                 .property('__dimensions__', [dimensions[0], dimensions[1]])
71404                 .attr('width', dimensions[0])
71405                 .attr('height', dimensions[1]);
71406         }
71407
71408         function svgLayers(projection, context) {
71409             var dispatch$1 = dispatch('change');
71410             var svg = select(null);
71411             var _layers = [
71412                 { id: 'osm', layer: svgOsm(projection, context, dispatch$1) },
71413                 { id: 'notes', layer: svgNotes(projection, context, dispatch$1) },
71414                 { id: 'data', layer: svgData(projection, context, dispatch$1) },
71415                 { id: 'keepRight', layer: svgKeepRight(projection, context, dispatch$1) },
71416                 { id: 'improveOSM', layer: svgImproveOSM(projection, context, dispatch$1) },
71417                 { id: 'osmose', layer: svgOsmose(projection, context, dispatch$1) },
71418                 { id: 'streetside', layer: svgStreetside(projection, context, dispatch$1)},
71419                 { id: 'mapillary', layer: svgMapillaryImages(projection, context, dispatch$1) },
71420                 { id: 'mapillary-map-features',  layer: svgMapillaryMapFeatures(projection, context, dispatch$1) },
71421                 { id: 'mapillary-signs',  layer: svgMapillarySigns(projection, context, dispatch$1) },
71422                 { id: 'openstreetcam', layer: svgOpenstreetcamImages(projection, context, dispatch$1) },
71423                 { id: 'debug', layer: svgDebug(projection, context) },
71424                 { id: 'geolocate', layer: svgGeolocate(projection) },
71425                 { id: 'touch', layer: svgTouch() }
71426             ];
71427
71428
71429             function drawLayers(selection) {
71430                 svg = selection.selectAll('.surface')
71431                     .data([0]);
71432
71433                 svg = svg.enter()
71434                     .append('svg')
71435                     .attr('class', 'surface')
71436                     .merge(svg);
71437
71438                 var defs = svg.selectAll('.surface-defs')
71439                     .data([0]);
71440
71441                 defs.enter()
71442                     .append('defs')
71443                     .attr('class', 'surface-defs');
71444
71445                 var groups = svg.selectAll('.data-layer')
71446                     .data(_layers);
71447
71448                 groups.exit()
71449                     .remove();
71450
71451                 groups.enter()
71452                     .append('g')
71453                     .attr('class', function(d) { return 'data-layer ' + d.id; })
71454                     .merge(groups)
71455                     .each(function(d) { select(this).call(d.layer); });
71456             }
71457
71458
71459             drawLayers.all = function() {
71460                 return _layers;
71461             };
71462
71463
71464             drawLayers.layer = function(id) {
71465                 var obj = _layers.find(function(o) { return o.id === id; });
71466                 return obj && obj.layer;
71467             };
71468
71469
71470             drawLayers.only = function(what) {
71471                 var arr = [].concat(what);
71472                 var all = _layers.map(function(layer) { return layer.id; });
71473                 return drawLayers.remove(utilArrayDifference(all, arr));
71474             };
71475
71476
71477             drawLayers.remove = function(what) {
71478                 var arr = [].concat(what);
71479                 arr.forEach(function(id) {
71480                     _layers = _layers.filter(function(o) { return o.id !== id; });
71481                 });
71482                 dispatch$1.call('change');
71483                 return this;
71484             };
71485
71486
71487             drawLayers.add = function(what) {
71488                 var arr = [].concat(what);
71489                 arr.forEach(function(obj) {
71490                     if ('id' in obj && 'layer' in obj) {
71491                         _layers.push(obj);
71492                     }
71493                 });
71494                 dispatch$1.call('change');
71495                 return this;
71496             };
71497
71498
71499             drawLayers.dimensions = function(val) {
71500                 if (!arguments.length) return utilGetDimensions(svg);
71501                 utilSetDimensions(svg, val);
71502                 return this;
71503             };
71504
71505
71506             return utilRebind(drawLayers, dispatch$1, 'on');
71507         }
71508
71509         function svgLines(projection, context) {
71510             var detected = utilDetect();
71511
71512             var highway_stack = {
71513                 motorway: 0,
71514                 motorway_link: 1,
71515                 trunk: 2,
71516                 trunk_link: 3,
71517                 primary: 4,
71518                 primary_link: 5,
71519                 secondary: 6,
71520                 tertiary: 7,
71521                 unclassified: 8,
71522                 residential: 9,
71523                 service: 10,
71524                 footway: 11
71525             };
71526
71527
71528             function drawTargets(selection, graph, entities, filter) {
71529                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71530                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
71531                 var getPath = svgPath(projection).geojson;
71532                 var activeID = context.activeID();
71533                 var base = context.history().base();
71534
71535                 // The targets and nopes will be MultiLineString sub-segments of the ways
71536                 var data = { targets: [], nopes: [] };
71537
71538                 entities.forEach(function(way) {
71539                     var features = svgSegmentWay(way, graph, activeID);
71540                     data.targets.push.apply(data.targets, features.passive);
71541                     data.nopes.push.apply(data.nopes, features.active);
71542                 });
71543
71544
71545                 // Targets allow hover and vertex snapping
71546                 var targetData = data.targets.filter(getPath);
71547                 var targets = selection.selectAll('.line.target-allowed')
71548                     .filter(function(d) { return filter(d.properties.entity); })
71549                     .data(targetData, function key(d) { return d.id; });
71550
71551                 // exit
71552                 targets.exit()
71553                     .remove();
71554
71555                 var segmentWasEdited = function(d) {
71556                     var wayID = d.properties.entity.id;
71557                     // if the whole line was edited, don't draw segment changes
71558                     if (!base.entities[wayID] ||
71559                         !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
71560                         return false;
71561                     }
71562                     return d.properties.nodes.some(function(n) {
71563                         return !base.entities[n.id] ||
71564                                !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
71565                     });
71566                 };
71567
71568                 // enter/update
71569                 targets.enter()
71570                     .append('path')
71571                     .merge(targets)
71572                     .attr('d', getPath)
71573                     .attr('class', function(d) {
71574                         return 'way line target target-allowed ' + targetClass + d.id;
71575                     })
71576                     .classed('segment-edited', segmentWasEdited);
71577
71578                 // NOPE
71579                 var nopeData = data.nopes.filter(getPath);
71580                 var nopes = selection.selectAll('.line.target-nope')
71581                     .filter(function(d) { return filter(d.properties.entity); })
71582                     .data(nopeData, function key(d) { return d.id; });
71583
71584                 // exit
71585                 nopes.exit()
71586                     .remove();
71587
71588                 // enter/update
71589                 nopes.enter()
71590                     .append('path')
71591                     .merge(nopes)
71592                     .attr('d', getPath)
71593                     .attr('class', function(d) {
71594                         return 'way line target target-nope ' + nopeClass + d.id;
71595                     })
71596                     .classed('segment-edited', segmentWasEdited);
71597             }
71598
71599
71600             function drawLines(selection, graph, entities, filter) {
71601                 var base = context.history().base();
71602
71603                 function waystack(a, b) {
71604                     var selected = context.selectedIDs();
71605                     var scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0;
71606                     var scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0;
71607
71608                     if (a.tags.highway) { scoreA -= highway_stack[a.tags.highway]; }
71609                     if (b.tags.highway) { scoreB -= highway_stack[b.tags.highway]; }
71610                     return scoreA - scoreB;
71611                 }
71612
71613
71614                 function drawLineGroup(selection, klass, isSelected) {
71615                     // Note: Don't add `.selected` class in draw modes
71616                     var mode = context.mode();
71617                     var isDrawing = mode && /^draw/.test(mode.id);
71618                     var selectedClass = (!isDrawing && isSelected) ? 'selected ' : '';
71619
71620                     var lines = selection
71621                         .selectAll('path')
71622                         .filter(filter)
71623                         .data(getPathData(isSelected), osmEntity.key);
71624
71625                     lines.exit()
71626                         .remove();
71627
71628                     // Optimization: Call expensive TagClasses only on enter selection. This
71629                     // works because osmEntity.key is defined to include the entity v attribute.
71630                     lines.enter()
71631                         .append('path')
71632                         .attr('class', function(d) {
71633
71634                             var prefix = 'way line';
71635
71636                             // if this line isn't styled by its own tags
71637                             if (!d.hasInterestingTags()) {
71638
71639                                 var parentRelations = graph.parentRelations(d);
71640                                 var parentMultipolygons = parentRelations.filter(function(relation) {
71641                                     return relation.isMultipolygon();
71642                                 });
71643
71644                                 // and if it's a member of at least one multipolygon relation
71645                                 if (parentMultipolygons.length > 0 &&
71646                                     // and only multipolygon relations
71647                                     parentRelations.length === parentMultipolygons.length) {
71648                                     // then fudge the classes to style this as an area edge
71649                                     prefix = 'relation area';
71650                                 }
71651                             }
71652
71653                             var oldMPClass = oldMultiPolygonOuters[d.id] ? 'old-multipolygon ' : '';
71654                             return prefix + ' ' + klass + ' ' + selectedClass + oldMPClass + d.id;
71655                         })
71656                         .classed('added', function(d) {
71657                             return !base.entities[d.id];
71658                         })
71659                         .classed('geometry-edited', function(d) {
71660                             return graph.entities[d.id] &&
71661                                 base.entities[d.id] &&
71662                                 !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
71663                         })
71664                         .classed('retagged', function(d) {
71665                             return graph.entities[d.id] &&
71666                                 base.entities[d.id] &&
71667                                 !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
71668                         })
71669                         .call(svgTagClasses())
71670                         .merge(lines)
71671                         .sort(waystack)
71672                         .attr('d', getPath)
71673                         .call(svgTagClasses().tags(svgRelationMemberTags(graph)));
71674
71675                     return selection;
71676                 }
71677
71678
71679                 function getPathData(isSelected) {
71680                     return function() {
71681                         var layer = this.parentNode.__data__;
71682                         var data = pathdata[layer] || [];
71683                         return data.filter(function(d) {
71684                             if (isSelected)
71685                                 return context.selectedIDs().indexOf(d.id) !== -1;
71686                             else
71687                                 return context.selectedIDs().indexOf(d.id) === -1;
71688                         });
71689                     };
71690                 }
71691
71692                 function addMarkers(layergroup, pathclass, groupclass, groupdata, marker) {
71693                     var markergroup = layergroup
71694                         .selectAll('g.' + groupclass)
71695                         .data([pathclass]);
71696
71697                     markergroup = markergroup.enter()
71698                         .append('g')
71699                         .attr('class', groupclass)
71700                         .merge(markergroup);
71701
71702                     var markers = markergroup
71703                         .selectAll('path')
71704                         .filter(filter)
71705                         .data(
71706                             function data() { return groupdata[this.parentNode.__data__] || []; },
71707                             function key(d) { return [d.id, d.index]; }
71708                         );
71709
71710                     markers.exit()
71711                         .remove();
71712
71713                     markers = markers.enter()
71714                         .append('path')
71715                         .attr('class', pathclass)
71716                         .merge(markers)
71717                         .attr('marker-mid', marker)
71718                         .attr('d', function(d) { return d.d; });
71719
71720                     if (detected.ie) {
71721                         markers.each(function() { this.parentNode.insertBefore(this, this); });
71722                     }
71723                 }
71724
71725
71726                 var getPath = svgPath(projection, graph);
71727                 var ways = [];
71728                 var onewaydata = {};
71729                 var sideddata = {};
71730                 var oldMultiPolygonOuters = {};
71731
71732                 for (var i = 0; i < entities.length; i++) {
71733                     var entity = entities[i];
71734                     var outer = osmOldMultipolygonOuterMember(entity, graph);
71735                     if (outer) {
71736                         ways.push(entity.mergeTags(outer.tags));
71737                         oldMultiPolygonOuters[outer.id] = true;
71738                     } else if (entity.geometry(graph) === 'line') {
71739                         ways.push(entity);
71740                     }
71741                 }
71742
71743                 ways = ways.filter(getPath);
71744                 var pathdata = utilArrayGroupBy(ways, function(way) { return way.layer(); });
71745
71746                 Object.keys(pathdata).forEach(function(k) {
71747                     var v = pathdata[k];
71748                     var onewayArr = v.filter(function(d) { return d.isOneWay(); });
71749                     var onewaySegments = svgMarkerSegments(
71750                         projection, graph, 35,
71751                         function shouldReverse(entity) { return entity.tags.oneway === '-1'; },
71752                         function bothDirections(entity) {
71753                             return entity.tags.oneway === 'reversible' || entity.tags.oneway === 'alternating';
71754                         }
71755                     );
71756                     onewaydata[k] = utilArrayFlatten(onewayArr.map(onewaySegments));
71757
71758                     var sidedArr = v.filter(function(d) { return d.isSided(); });
71759                     var sidedSegments = svgMarkerSegments(
71760                         projection, graph, 30,
71761                         function shouldReverse() { return false; },
71762                         function bothDirections() { return false; }
71763                     );
71764                     sideddata[k] = utilArrayFlatten(sidedArr.map(sidedSegments));
71765                 });
71766
71767
71768                 var covered = selection.selectAll('.layer-osm.covered');     // under areas
71769                 var uncovered = selection.selectAll('.layer-osm.lines');     // over areas
71770                 var touchLayer = selection.selectAll('.layer-touch.lines');
71771
71772                 // Draw lines..
71773                 [covered, uncovered].forEach(function(selection) {
71774                     var range = (selection === covered ? range$1(-10,0) : range$1(0,11));
71775                     var layergroup = selection
71776                         .selectAll('g.layergroup')
71777                         .data(range);
71778
71779                     layergroup = layergroup.enter()
71780                         .append('g')
71781                         .attr('class', function(d) { return 'layergroup layer' + String(d); })
71782                         .merge(layergroup);
71783
71784                     layergroup
71785                         .selectAll('g.linegroup')
71786                         .data(['shadow', 'casing', 'stroke', 'shadow-highlighted', 'casing-highlighted', 'stroke-highlighted'])
71787                         .enter()
71788                         .append('g')
71789                         .attr('class', function(d) { return 'linegroup line-' + d; });
71790
71791                     layergroup.selectAll('g.line-shadow')
71792                         .call(drawLineGroup, 'shadow', false);
71793                     layergroup.selectAll('g.line-casing')
71794                         .call(drawLineGroup, 'casing', false);
71795                     layergroup.selectAll('g.line-stroke')
71796                         .call(drawLineGroup, 'stroke', false);
71797
71798                     layergroup.selectAll('g.line-shadow-highlighted')
71799                         .call(drawLineGroup, 'shadow', true);
71800                     layergroup.selectAll('g.line-casing-highlighted')
71801                         .call(drawLineGroup, 'casing', true);
71802                     layergroup.selectAll('g.line-stroke-highlighted')
71803                         .call(drawLineGroup, 'stroke', true);
71804
71805                     addMarkers(layergroup, 'oneway', 'onewaygroup', onewaydata, 'url(#ideditor-oneway-marker)');
71806                     addMarkers(layergroup, 'sided', 'sidedgroup', sideddata,
71807                         function marker(d) {
71808                             var category = graph.entity(d.id).sidednessIdentifier();
71809                             return 'url(#ideditor-sided-marker-' + category + ')';
71810                         }
71811                     );
71812                 });
71813
71814                 // Draw touch targets..
71815                 touchLayer
71816                     .call(drawTargets, graph, ways, filter);
71817             }
71818
71819
71820             return drawLines;
71821         }
71822
71823         function svgMidpoints(projection, context) {
71824             var targetRadius = 8;
71825
71826             function drawTargets(selection, graph, entities, filter) {
71827                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71828                 var getTransform = svgPointTransform(projection).geojson;
71829
71830                 var data = entities.map(function(midpoint) {
71831                     return {
71832                         type: 'Feature',
71833                         id: midpoint.id,
71834                         properties: {
71835                             target: true,
71836                             entity: midpoint
71837                         },
71838                         geometry: {
71839                             type: 'Point',
71840                             coordinates: midpoint.loc
71841                         }
71842                     };
71843                 });
71844
71845                 var targets = selection.selectAll('.midpoint.target')
71846                     .filter(function(d) { return filter(d.properties.entity); })
71847                     .data(data, function key(d) { return d.id; });
71848
71849                 // exit
71850                 targets.exit()
71851                     .remove();
71852
71853                 // enter/update
71854                 targets.enter()
71855                     .append('circle')
71856                     .attr('r', targetRadius)
71857                     .merge(targets)
71858                     .attr('class', function(d) { return 'node midpoint target ' + fillClass + d.id; })
71859                     .attr('transform', getTransform);
71860             }
71861
71862
71863             function drawMidpoints(selection, graph, entities, filter, extent) {
71864                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.midpoints');
71865                 var touchLayer = selection.selectAll('.layer-touch.points');
71866
71867                 var mode = context.mode();
71868                 if ((mode && mode.id !== 'select') || !context.map().withinEditableZoom()) {
71869                     drawLayer.selectAll('.midpoint').remove();
71870                     touchLayer.selectAll('.midpoint.target').remove();
71871                     return;
71872                 }
71873
71874                 var poly = extent.polygon();
71875                 var midpoints = {};
71876
71877                 for (var i = 0; i < entities.length; i++) {
71878                     var entity = entities[i];
71879
71880                     if (entity.type !== 'way') continue;
71881                     if (!filter(entity)) continue;
71882                     if (context.selectedIDs().indexOf(entity.id) < 0) continue;
71883
71884                     var nodes = graph.childNodes(entity);
71885                     for (var j = 0; j < nodes.length - 1; j++) {
71886                         var a = nodes[j];
71887                         var b = nodes[j + 1];
71888                         var id = [a.id, b.id].sort().join('-');
71889
71890                         if (midpoints[id]) {
71891                             midpoints[id].parents.push(entity);
71892                         } else if (geoVecLength(projection(a.loc), projection(b.loc)) > 40) {
71893                             var point = geoVecInterp(a.loc, b.loc, 0.5);
71894                             var loc = null;
71895
71896                             if (extent.intersects(point)) {
71897                                 loc = point;
71898                             } else {
71899                                 for (var k = 0; k < 4; k++) {
71900                                     point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]);
71901                                     if (point &&
71902                                         geoVecLength(projection(a.loc), projection(point)) > 20 &&
71903                                         geoVecLength(projection(b.loc), projection(point)) > 20)
71904                                     {
71905                                         loc = point;
71906                                         break;
71907                                     }
71908                                 }
71909                             }
71910
71911                             if (loc) {
71912                                 midpoints[id] = {
71913                                     type: 'midpoint',
71914                                     id: id,
71915                                     loc: loc,
71916                                     edge: [a.id, b.id],
71917                                     parents: [entity]
71918                                 };
71919                             }
71920                         }
71921                     }
71922                 }
71923
71924
71925                 function midpointFilter(d) {
71926                     if (midpoints[d.id])
71927                         return true;
71928
71929                     for (var i = 0; i < d.parents.length; i++) {
71930                         if (filter(d.parents[i])) {
71931                             return true;
71932                         }
71933                     }
71934
71935                     return false;
71936                 }
71937
71938
71939                 var groups = drawLayer.selectAll('.midpoint')
71940                     .filter(midpointFilter)
71941                     .data(Object.values(midpoints), function(d) { return d.id; });
71942
71943                 groups.exit()
71944                     .remove();
71945
71946                 var enter = groups.enter()
71947                     .insert('g', ':first-child')
71948                     .attr('class', 'midpoint');
71949
71950                 enter
71951                     .append('polygon')
71952                     .attr('points', '-6,8 10,0 -6,-8')
71953                     .attr('class', 'shadow');
71954
71955                 enter
71956                     .append('polygon')
71957                     .attr('points', '-3,4 5,0 -3,-4')
71958                     .attr('class', 'fill');
71959
71960                 groups = groups
71961                     .merge(enter)
71962                     .attr('transform', function(d) {
71963                         var translate = svgPointTransform(projection);
71964                         var a = graph.entity(d.edge[0]);
71965                         var b = graph.entity(d.edge[1]);
71966                         var angle = geoAngle(a, b, projection) * (180 / Math.PI);
71967                         return translate(d) + ' rotate(' + angle + ')';
71968                     })
71969                     .call(svgTagClasses().tags(
71970                         function(d) { return d.parents[0].tags; }
71971                     ));
71972
71973                 // Propagate data bindings.
71974                 groups.select('polygon.shadow');
71975                 groups.select('polygon.fill');
71976
71977
71978                 // Draw touch targets..
71979                 touchLayer
71980                     .call(drawTargets, graph, Object.values(midpoints), midpointFilter);
71981             }
71982
71983             return drawMidpoints;
71984         }
71985
71986         function svgPoints(projection, context) {
71987
71988             function markerPath(selection, klass) {
71989                 selection
71990                     .attr('class', klass)
71991                     .attr('transform', 'translate(-8, -23)')
71992                     .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');
71993             }
71994
71995             function sortY(a, b) {
71996                 return b.loc[1] - a.loc[1];
71997             }
71998
71999
72000             // Avoid exit/enter if we're just moving stuff around.
72001             // The node will get a new version but we only need to run the update selection.
72002             function fastEntityKey(d) {
72003                 var mode = context.mode();
72004                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
72005                 return isMoving ? d.id : osmEntity.key(d);
72006             }
72007
72008
72009             function drawTargets(selection, graph, entities, filter) {
72010                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72011                 var getTransform = svgPointTransform(projection).geojson;
72012                 var activeID = context.activeID();
72013                 var data = [];
72014
72015                 entities.forEach(function(node) {
72016                     if (activeID === node.id) return;   // draw no target on the activeID
72017
72018                     data.push({
72019                         type: 'Feature',
72020                         id: node.id,
72021                         properties: {
72022                             target: true,
72023                             entity: node
72024                         },
72025                         geometry: node.asGeoJSON()
72026                     });
72027                 });
72028
72029                 var targets = selection.selectAll('.point.target')
72030                     .filter(function(d) { return filter(d.properties.entity); })
72031                     .data(data, function key(d) { return d.id; });
72032
72033                 // exit
72034                 targets.exit()
72035                     .remove();
72036
72037                 // enter/update
72038                 targets.enter()
72039                     .append('rect')
72040                     .attr('x', -10)
72041                     .attr('y', -26)
72042                     .attr('width', 20)
72043                     .attr('height', 30)
72044                     .merge(targets)
72045                     .attr('class', function(d) { return 'node point target ' + fillClass + d.id; })
72046                     .attr('transform', getTransform);
72047             }
72048
72049
72050             function drawPoints(selection, graph, entities, filter) {
72051                 var wireframe = context.surface().classed('fill-wireframe');
72052                 var zoom = geoScaleToZoom(projection.scale());
72053                 var base = context.history().base();
72054
72055                 // Points with a direction will render as vertices at higher zooms..
72056                 function renderAsPoint(entity) {
72057                     return entity.geometry(graph) === 'point' &&
72058                         !(zoom >= 18 && entity.directions(graph, projection).length);
72059                 }
72060
72061                 // All points will render as vertices in wireframe mode too..
72062                 var points = wireframe ? [] : entities.filter(renderAsPoint);
72063                 points.sort(sortY);
72064
72065
72066                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.points');
72067                 var touchLayer = selection.selectAll('.layer-touch.points');
72068
72069                 // Draw points..
72070                 var groups = drawLayer.selectAll('g.point')
72071                     .filter(filter)
72072                     .data(points, fastEntityKey);
72073
72074                 groups.exit()
72075                     .remove();
72076
72077                 var enter = groups.enter()
72078                     .append('g')
72079                     .attr('class', function(d) { return 'node point ' + d.id; })
72080                     .order();
72081
72082                 enter
72083                     .append('path')
72084                     .call(markerPath, 'shadow');
72085
72086                 enter
72087                     .append('ellipse')
72088                     .attr('cx', 0.5)
72089                     .attr('cy', 1)
72090                     .attr('rx', 6.5)
72091                     .attr('ry', 3)
72092                     .attr('class', 'stroke');
72093
72094                 enter
72095                     .append('path')
72096                     .call(markerPath, 'stroke');
72097
72098                 enter
72099                     .append('use')
72100                     .attr('transform', 'translate(-5, -19)')
72101                     .attr('class', 'icon')
72102                     .attr('width', '11px')
72103                     .attr('height', '11px');
72104
72105                 groups = groups
72106                     .merge(enter)
72107                     .attr('transform', svgPointTransform(projection))
72108                     .classed('added', function(d) {
72109                         return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
72110                     })
72111                     .classed('moved', function(d) {
72112                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
72113                     })
72114                     .classed('retagged', function(d) {
72115                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
72116                     })
72117                     .call(svgTagClasses());
72118
72119                 groups.select('.shadow');   // propagate bound data
72120                 groups.select('.stroke');   // propagate bound data
72121                 groups.select('.icon')      // propagate bound data
72122                     .attr('xlink:href', function(entity) {
72123                         var preset = _mainPresetIndex.match(entity, graph);
72124                         var picon = preset && preset.icon;
72125
72126                         if (!picon) {
72127                             return '';
72128                         } else {
72129                             var isMaki = /^maki-/.test(picon);
72130                             return '#' + picon + (isMaki ? '-11' : '');
72131                         }
72132                     });
72133
72134
72135                 // Draw touch targets..
72136                 touchLayer
72137                     .call(drawTargets, graph, points, filter);
72138             }
72139
72140
72141             return drawPoints;
72142         }
72143
72144         function svgTurns(projection, context) {
72145
72146             function icon(turn) {
72147                 var u = turn.u ? '-u' : '';
72148                 if (turn.no) return '#iD-turn-no' + u;
72149                 if (turn.only) return '#iD-turn-only' + u;
72150                 return '#iD-turn-yes' + u;
72151             }
72152
72153             function drawTurns(selection, graph, turns) {
72154
72155                 function turnTransform(d) {
72156                     var pxRadius = 50;
72157                     var toWay = graph.entity(d.to.way);
72158                     var toPoints = graph.childNodes(toWay)
72159                         .map(function (n) { return n.loc; })
72160                         .map(projection);
72161                     var toLength = geoPathLength(toPoints);
72162                     var mid = toLength / 2;    // midpoint of destination way
72163
72164                     var toNode = graph.entity(d.to.node);
72165                     var toVertex = graph.entity(d.to.vertex);
72166                     var a = geoAngle(toVertex, toNode, projection);
72167                     var o = projection(toVertex.loc);
72168                     var r = d.u ? 0                  // u-turn: no radius
72169                         : !toWay.__via ? pxRadius    // leaf way: put marker at pxRadius
72170                         : Math.min(mid, pxRadius);   // via way: prefer pxRadius, fallback to mid for very short ways
72171
72172                     return 'translate(' + (r * Math.cos(a) + o[0]) + ',' + (r * Math.sin(a) + o[1]) + ') ' +
72173                         'rotate(' + a * 180 / Math.PI + ')';
72174                 }
72175
72176
72177                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.turns');
72178                 var touchLayer = selection.selectAll('.layer-touch.turns');
72179
72180                 // Draw turns..
72181                 var groups = drawLayer.selectAll('g.turn')
72182                     .data(turns, function(d) { return d.key; });
72183
72184                 // exit
72185                 groups.exit()
72186                     .remove();
72187
72188                 // enter
72189                 var groupsEnter = groups.enter()
72190                     .append('g')
72191                     .attr('class', function(d) { return 'turn ' + d.key; });
72192
72193                 var turnsEnter = groupsEnter
72194                     .filter(function(d) { return !d.u; });
72195
72196                 turnsEnter.append('rect')
72197                     .attr('transform', 'translate(-22, -12)')
72198                     .attr('width', '44')
72199                     .attr('height', '24');
72200
72201                 turnsEnter.append('use')
72202                     .attr('transform', 'translate(-22, -12)')
72203                     .attr('width', '44')
72204                     .attr('height', '24');
72205
72206                 var uEnter = groupsEnter
72207                     .filter(function(d) { return d.u; });
72208
72209                 uEnter.append('circle')
72210                     .attr('r', '16');
72211
72212                 uEnter.append('use')
72213                     .attr('transform', 'translate(-16, -16)')
72214                     .attr('width', '32')
72215                     .attr('height', '32');
72216
72217                 // update
72218                 groups = groups
72219                     .merge(groupsEnter)
72220                     .attr('opacity', function(d) { return d.direct === false ? '0.7' : null; })
72221                     .attr('transform', turnTransform);
72222
72223                 groups.select('use')
72224                     .attr('xlink:href', icon);
72225
72226                 groups.select('rect');      // propagate bound data
72227                 groups.select('circle');    // propagate bound data
72228
72229
72230                 // Draw touch targets..
72231                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72232                 groups = touchLayer.selectAll('g.turn')
72233                     .data(turns, function(d) { return d.key; });
72234
72235                 // exit
72236                 groups.exit()
72237                     .remove();
72238
72239                 // enter
72240                 groupsEnter = groups.enter()
72241                     .append('g')
72242                     .attr('class', function(d) { return 'turn ' + d.key; });
72243
72244                 turnsEnter = groupsEnter
72245                     .filter(function(d) { return !d.u; });
72246
72247                 turnsEnter.append('rect')
72248                     .attr('class', 'target ' + fillClass)
72249                     .attr('transform', 'translate(-22, -12)')
72250                     .attr('width', '44')
72251                     .attr('height', '24');
72252
72253                 uEnter = groupsEnter
72254                     .filter(function(d) { return d.u; });
72255
72256                 uEnter.append('circle')
72257                     .attr('class', 'target ' + fillClass)
72258                     .attr('r', '16');
72259
72260                 // update
72261                 groups = groups
72262                     .merge(groupsEnter)
72263                     .attr('transform', turnTransform);
72264
72265                 groups.select('rect');      // propagate bound data
72266                 groups.select('circle');    // propagate bound data
72267
72268
72269                 return this;
72270             }
72271
72272             return drawTurns;
72273         }
72274
72275         function svgVertices(projection, context) {
72276             var radiuses = {
72277                 //       z16-, z17,   z18+,  w/icon
72278                 shadow: [6,    7.5,   7.5,   12],
72279                 stroke: [2.5,  3.5,   3.5,   8],
72280                 fill:   [1,    1.5,   1.5,   1.5]
72281             };
72282
72283             var _currHoverTarget;
72284             var _currPersistent = {};
72285             var _currHover = {};
72286             var _prevHover = {};
72287             var _currSelected = {};
72288             var _prevSelected = {};
72289             var _radii = {};
72290
72291
72292             function sortY(a, b) {
72293                 return b.loc[1] - a.loc[1];
72294             }
72295
72296             // Avoid exit/enter if we're just moving stuff around.
72297             // The node will get a new version but we only need to run the update selection.
72298             function fastEntityKey(d) {
72299                 var mode = context.mode();
72300                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
72301                 return isMoving ? d.id : osmEntity.key(d);
72302             }
72303
72304
72305             function draw(selection, graph, vertices, sets, filter) {
72306                 sets = sets || { selected: {}, important: {}, hovered: {} };
72307
72308                 var icons = {};
72309                 var directions = {};
72310                 var wireframe = context.surface().classed('fill-wireframe');
72311                 var zoom = geoScaleToZoom(projection.scale());
72312                 var z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);
72313                 var activeID = context.activeID();
72314                 var base = context.history().base();
72315
72316
72317                 function getIcon(d) {
72318                     // always check latest entity, as fastEntityKey avoids enter/exit now
72319                     var entity = graph.entity(d.id);
72320                     if (entity.id in icons) return icons[entity.id];
72321
72322                     icons[entity.id] =
72323                         entity.hasInterestingTags() &&
72324                         _mainPresetIndex.match(entity, graph).icon;
72325
72326                     return icons[entity.id];
72327                 }
72328
72329
72330                 // memoize directions results, return false for empty arrays (for use in filter)
72331                 function getDirections(entity) {
72332                     if (entity.id in directions) return directions[entity.id];
72333
72334                     var angles = entity.directions(graph, projection);
72335                     directions[entity.id] = angles.length ? angles : false;
72336                     return angles;
72337                 }
72338
72339
72340                 function updateAttributes(selection) {
72341                     ['shadow', 'stroke', 'fill'].forEach(function(klass) {
72342                         var rads = radiuses[klass];
72343                         selection.selectAll('.' + klass)
72344                             .each(function(entity) {
72345                                 var i = z && getIcon(entity);
72346                                 var r = rads[i ? 3 : z];
72347
72348                                 // slightly increase the size of unconnected endpoints #3775
72349                                 if (entity.id !== activeID && entity.isEndpoint(graph) && !entity.isConnected(graph)) {
72350                                     r += 1.5;
72351                                 }
72352
72353                                 if (klass === 'shadow') {   // remember this value, so we don't need to
72354                                     _radii[entity.id] = r;  // recompute it when we draw the touch targets
72355                                 }
72356
72357                                 select(this)
72358                                     .attr('r', r)
72359                                     .attr('visibility', (i && klass === 'fill') ? 'hidden' : null);
72360                             });
72361                     });
72362                 }
72363
72364                 vertices.sort(sortY);
72365
72366                 var groups = selection.selectAll('g.vertex')
72367                     .filter(filter)
72368                     .data(vertices, fastEntityKey);
72369
72370                 // exit
72371                 groups.exit()
72372                     .remove();
72373
72374                 // enter
72375                 var enter = groups.enter()
72376                     .append('g')
72377                     .attr('class', function(d) { return 'node vertex ' + d.id; })
72378                     .order();
72379
72380                 enter
72381                     .append('circle')
72382                     .attr('class', 'shadow');
72383
72384                 enter
72385                     .append('circle')
72386                     .attr('class', 'stroke');
72387
72388                 // Vertices with tags get a fill.
72389                 enter.filter(function(d) { return d.hasInterestingTags(); })
72390                     .append('circle')
72391                     .attr('class', 'fill');
72392
72393                 // update
72394                 groups = groups
72395                     .merge(enter)
72396                     .attr('transform', svgPointTransform(projection))
72397                     .classed('sibling', function(d) { return d.id in sets.selected; })
72398                     .classed('shared', function(d) { return graph.isShared(d); })
72399                     .classed('endpoint', function(d) { return d.isEndpoint(graph); })
72400                     .classed('added', function(d) {
72401                         return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
72402                     })
72403                     .classed('moved', function(d) {
72404                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
72405                     })
72406                     .classed('retagged', function(d) {
72407                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
72408                     })
72409                     .call(updateAttributes);
72410
72411                 // Vertices with icons get a `use`.
72412                 var iconUse = groups
72413                     .selectAll('.icon')
72414                     .data(function data(d) { return zoom >= 17 && getIcon(d) ? [d] : []; }, fastEntityKey);
72415
72416                 // exit
72417                 iconUse.exit()
72418                     .remove();
72419
72420                 // enter
72421                 iconUse.enter()
72422                     .append('use')
72423                     .attr('class', 'icon')
72424                     .attr('width', '11px')
72425                     .attr('height', '11px')
72426                     .attr('transform', 'translate(-5.5, -5.5)')
72427                     .attr('xlink:href', function(d) {
72428                         var picon = getIcon(d);
72429                         var isMaki = /^maki-/.test(picon);
72430                         return '#' + picon + (isMaki ? '-11' : '');
72431                     });
72432
72433
72434                 // Vertices with directions get viewfields
72435                 var dgroups = groups
72436                     .selectAll('.viewfieldgroup')
72437                     .data(function data(d) { return zoom >= 18 && getDirections(d) ? [d] : []; }, fastEntityKey);
72438
72439                 // exit
72440                 dgroups.exit()
72441                     .remove();
72442
72443                 // enter/update
72444                 dgroups = dgroups.enter()
72445                     .insert('g', '.shadow')
72446                     .attr('class', 'viewfieldgroup')
72447                     .merge(dgroups);
72448
72449                 var viewfields = dgroups.selectAll('.viewfield')
72450                     .data(getDirections, function key(d) { return osmEntity.key(d); });
72451
72452                 // exit
72453                 viewfields.exit()
72454                     .remove();
72455
72456                 // enter/update
72457                 viewfields.enter()
72458                     .append('path')
72459                     .attr('class', 'viewfield')
72460                     .attr('d', 'M0,0H0')
72461                     .merge(viewfields)
72462                     .attr('marker-start', 'url(#ideditor-viewfield-marker' + (wireframe ? '-wireframe' : '') + ')')
72463                     .attr('transform', function(d) { return 'rotate(' + d + ')'; });
72464             }
72465
72466
72467             function drawTargets(selection, graph, entities, filter) {
72468                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72469                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
72470                 var getTransform = svgPointTransform(projection).geojson;
72471                 var activeID = context.activeID();
72472                 var data = { targets: [], nopes: [] };
72473
72474                 entities.forEach(function(node) {
72475                     if (activeID === node.id) return;   // draw no target on the activeID
72476
72477                     var vertexType = svgPassiveVertex(node, graph, activeID);
72478                     if (vertexType !== 0) {     // passive or adjacent - allow to connect
72479                         data.targets.push({
72480                             type: 'Feature',
72481                             id: node.id,
72482                             properties: {
72483                                 target: true,
72484                                 entity: node
72485                             },
72486                             geometry: node.asGeoJSON()
72487                         });
72488                     } else {
72489                         data.nopes.push({
72490                             type: 'Feature',
72491                             id: node.id + '-nope',
72492                             properties: {
72493                                 nope: true,
72494                                 target: true,
72495                                 entity: node
72496                             },
72497                             geometry: node.asGeoJSON()
72498                         });
72499                     }
72500                 });
72501
72502                 // Targets allow hover and vertex snapping
72503                 var targets = selection.selectAll('.vertex.target-allowed')
72504                     .filter(function(d) { return filter(d.properties.entity); })
72505                     .data(data.targets, function key(d) { return d.id; });
72506
72507                 // exit
72508                 targets.exit()
72509                     .remove();
72510
72511                 // enter/update
72512                 targets.enter()
72513                     .append('circle')
72514                     .attr('r', function(d) {
72515                         return _radii[d.id]
72516                           || radiuses.shadow[3];
72517                     })
72518                     .merge(targets)
72519                     .attr('class', function(d) {
72520                         return 'node vertex target target-allowed '
72521                         + targetClass + d.id;
72522                     })
72523                     .attr('transform', getTransform);
72524
72525
72526                 // NOPE
72527                 var nopes = selection.selectAll('.vertex.target-nope')
72528                     .filter(function(d) { return filter(d.properties.entity); })
72529                     .data(data.nopes, function key(d) { return d.id; });
72530
72531                 // exit
72532                 nopes.exit()
72533                     .remove();
72534
72535                 // enter/update
72536                 nopes.enter()
72537                     .append('circle')
72538                     .attr('r', function(d) { return (_radii[d.properties.entity.id] || radiuses.shadow[3]); })
72539                     .merge(nopes)
72540                     .attr('class', function(d) { return 'node vertex target target-nope ' + nopeClass + d.id; })
72541                     .attr('transform', getTransform);
72542             }
72543
72544
72545             // Points can also render as vertices:
72546             // 1. in wireframe mode or
72547             // 2. at higher zooms if they have a direction
72548             function renderAsVertex(entity, graph, wireframe, zoom) {
72549                 var geometry = entity.geometry(graph);
72550                 return geometry === 'vertex' || (geometry === 'point' && (
72551                     wireframe || (zoom >= 18 && entity.directions(graph, projection).length)
72552                 ));
72553             }
72554
72555
72556             function isEditedNode(node, base, head) {
72557                 var baseNode = base.entities[node.id];
72558                 var headNode = head.entities[node.id];
72559                 return !headNode ||
72560                     !baseNode ||
72561                     !fastDeepEqual(headNode.tags, baseNode.tags) ||
72562                     !fastDeepEqual(headNode.loc, baseNode.loc);
72563             }
72564
72565
72566             function getSiblingAndChildVertices(ids, graph, wireframe, zoom) {
72567                 var results = {};
72568
72569                 var seenIds = {};
72570
72571                 function addChildVertices(entity) {
72572
72573                     // avoid redunant work and infinite recursion of circular relations
72574                     if (seenIds[entity.id]) return;
72575                     seenIds[entity.id] = true;
72576
72577                     var geometry = entity.geometry(graph);
72578                     if (!context.features().isHiddenFeature(entity, graph, geometry)) {
72579                         var i;
72580                         if (entity.type === 'way') {
72581                             for (i = 0; i < entity.nodes.length; i++) {
72582                                 var child = graph.hasEntity(entity.nodes[i]);
72583                                 if (child) {
72584                                     addChildVertices(child);
72585                                 }
72586                             }
72587                         } else if (entity.type === 'relation') {
72588                             for (i = 0; i < entity.members.length; i++) {
72589                                 var member = graph.hasEntity(entity.members[i].id);
72590                                 if (member) {
72591                                     addChildVertices(member);
72592                                 }
72593                             }
72594                         } else if (renderAsVertex(entity, graph, wireframe, zoom)) {
72595                             results[entity.id] = entity;
72596                         }
72597                     }
72598                 }
72599
72600                 ids.forEach(function(id) {
72601                     var entity = graph.hasEntity(id);
72602                     if (!entity) return;
72603
72604                     if (entity.type === 'node') {
72605                         if (renderAsVertex(entity, graph, wireframe, zoom)) {
72606                             results[entity.id] = entity;
72607                             graph.parentWays(entity).forEach(function(entity) {
72608                                 addChildVertices(entity);
72609                             });
72610                         }
72611                     } else {  // way, relation
72612                         addChildVertices(entity);
72613                     }
72614                 });
72615
72616                 return results;
72617             }
72618
72619
72620             function drawVertices(selection, graph, entities, filter, extent, fullRedraw) {
72621                 var wireframe = context.surface().classed('fill-wireframe');
72622                 var visualDiff = context.surface().classed('highlight-edited');
72623                 var zoom = geoScaleToZoom(projection.scale());
72624                 var mode = context.mode();
72625                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
72626                 var base = context.history().base();
72627
72628                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.vertices');
72629                 var touchLayer = selection.selectAll('.layer-touch.points');
72630
72631                 if (fullRedraw) {
72632                     _currPersistent = {};
72633                     _radii = {};
72634                 }
72635
72636                 // Collect important vertices from the `entities` list..
72637                 // (during a paritial redraw, it will not contain everything)
72638                 for (var i = 0; i < entities.length; i++) {
72639                     var entity = entities[i];
72640                     var geometry = entity.geometry(graph);
72641                     var keep = false;
72642
72643                     // a point that looks like a vertex..
72644                     if ((geometry === 'point') && renderAsVertex(entity, graph, wireframe, zoom)) {
72645                         _currPersistent[entity.id] = entity;
72646                         keep = true;
72647
72648                     // a vertex of some importance..
72649                     } else if (geometry === 'vertex' &&
72650                         (entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph)
72651                         || (visualDiff && isEditedNode(entity, base, graph)))) {
72652                         _currPersistent[entity.id] = entity;
72653                         keep = true;
72654                     }
72655
72656                     // whatever this is, it's not a persistent vertex..
72657                     if (!keep && !fullRedraw) {
72658                         delete _currPersistent[entity.id];
72659                     }
72660                 }
72661
72662                 // 3 sets of vertices to consider:
72663                 var sets = {
72664                     persistent: _currPersistent,  // persistent = important vertices (render always)
72665                     selected: _currSelected,      // selected + siblings of selected (render always)
72666                     hovered: _currHover           // hovered + siblings of hovered (render only in draw modes)
72667                 };
72668
72669                 var all = Object.assign({}, (isMoving ? _currHover : {}), _currSelected, _currPersistent);
72670
72671                 // Draw the vertices..
72672                 // The filter function controls the scope of what objects d3 will touch (exit/enter/update)
72673                 // Adjust the filter function to expand the scope beyond whatever entities were passed in.
72674                 var filterRendered = function(d) {
72675                     return d.id in _currPersistent || d.id in _currSelected || d.id in _currHover || filter(d);
72676                 };
72677                 drawLayer
72678                     .call(draw, graph, currentVisible(all), sets, filterRendered);
72679
72680                 // Draw touch targets..
72681                 // When drawing, render all targets (not just those affected by a partial redraw)
72682                 var filterTouch = function(d) {
72683                     return isMoving ? true : filterRendered(d);
72684                 };
72685                 touchLayer
72686                     .call(drawTargets, graph, currentVisible(all), filterTouch);
72687
72688
72689                 function currentVisible(which) {
72690                     return Object.keys(which)
72691                         .map(graph.hasEntity, graph)     // the current version of this entity
72692                         .filter(function (entity) { return entity && entity.intersects(extent, graph); });
72693                 }
72694             }
72695
72696
72697             // partial redraw - only update the selected items..
72698             drawVertices.drawSelected = function(selection, graph, extent) {
72699                 var wireframe = context.surface().classed('fill-wireframe');
72700                 var zoom = geoScaleToZoom(projection.scale());
72701
72702                 _prevSelected = _currSelected || {};
72703                 if (context.map().isInWideSelection()) {
72704                     _currSelected = {};
72705                     context.selectedIDs().forEach(function(id) {
72706                         var entity = graph.hasEntity(id);
72707                         if (!entity) return;
72708
72709                         if (entity.type === 'node') {
72710                             if (renderAsVertex(entity, graph, wireframe, zoom)) {
72711                                 _currSelected[entity.id] = entity;
72712                             }
72713                         }
72714                     });
72715
72716                 } else {
72717                     _currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom);
72718                 }
72719
72720                 // note that drawVertices will add `_currSelected` automatically if needed..
72721                 var filter = function(d) { return d.id in _prevSelected; };
72722                 drawVertices(selection, graph, Object.values(_prevSelected), filter, extent, false);
72723             };
72724
72725
72726             // partial redraw - only update the hovered items..
72727             drawVertices.drawHover = function(selection, graph, target, extent) {
72728                 if (target === _currHoverTarget) return;  // continue only if something changed
72729
72730                 var wireframe = context.surface().classed('fill-wireframe');
72731                 var zoom = geoScaleToZoom(projection.scale());
72732
72733                 _prevHover = _currHover || {};
72734                 _currHoverTarget = target;
72735                 var entity = target && target.properties && target.properties.entity;
72736
72737                 if (entity) {
72738                     _currHover = getSiblingAndChildVertices([entity.id], graph, wireframe, zoom);
72739                 } else {
72740                     _currHover = {};
72741                 }
72742
72743                 // note that drawVertices will add `_currHover` automatically if needed..
72744                 var filter = function(d) { return d.id in _prevHover; };
72745                 drawVertices(selection, graph, Object.values(_prevHover), filter, extent, false);
72746             };
72747
72748             return drawVertices;
72749         }
72750
72751         function utilBindOnce(target, type, listener, capture) {
72752             var typeOnce = type + '.once';
72753             function one() {
72754                 target.on(typeOnce, null);
72755                 listener.apply(this, arguments);
72756             }
72757             target.on(typeOnce, one, capture);
72758             return this;
72759         }
72760
72761         // Adapted from d3-zoom to handle pointer events.
72762
72763         // Ignore right-click, since that should open the context menu.
72764         function defaultFilter$2() {
72765           return !event.ctrlKey && !event.button;
72766         }
72767
72768         function defaultExtent$1() {
72769           var e = this;
72770           if (e instanceof SVGElement) {
72771             e = e.ownerSVGElement || e;
72772             if (e.hasAttribute('viewBox')) {
72773               e = e.viewBox.baseVal;
72774               return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
72775             }
72776             return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
72777           }
72778           return [[0, 0], [e.clientWidth, e.clientHeight]];
72779         }
72780
72781         function defaultWheelDelta$1() {
72782           return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002);
72783         }
72784
72785         function defaultConstrain$1(transform, extent, translateExtent) {
72786           var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
72787               dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
72788               dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
72789               dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
72790           return transform.translate(
72791             dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
72792             dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
72793           );
72794         }
72795
72796         function utilZoomPan() {
72797           var filter = defaultFilter$2,
72798               extent = defaultExtent$1,
72799               constrain = defaultConstrain$1,
72800               wheelDelta = defaultWheelDelta$1,
72801               scaleExtent = [0, Infinity],
72802               translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
72803               interpolate = interpolateZoom,
72804               listeners = dispatch('start', 'zoom', 'end'),
72805               _wheelDelay = 150,
72806               _transform = identity$2,
72807               _activeGesture;
72808
72809           function zoom(selection) {
72810             selection
72811                 .on('pointerdown.zoom', pointerdown)
72812                 .on('wheel.zoom', wheeled)
72813                 .style('touch-action', 'none')
72814                 .style('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
72815
72816             select(window)
72817                 .on('pointermove.zoompan', pointermove)
72818                 .on('pointerup.zoompan pointercancel.zoompan', pointerup);
72819           }
72820
72821           zoom.transform = function(collection, transform, point) {
72822             var selection = collection.selection ? collection.selection() : collection;
72823             if (collection !== selection) {
72824               schedule(collection, transform, point);
72825             } else {
72826               selection.interrupt().each(function() {
72827                 gesture(this, arguments)
72828                     .start()
72829                     .zoom(null, typeof transform === 'function' ? transform.apply(this, arguments) : transform)
72830                     .end();
72831               });
72832             }
72833           };
72834
72835           zoom.scaleBy = function(selection, k, p) {
72836             zoom.scaleTo(selection, function() {
72837               var k0 = _transform.k,
72838                   k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
72839               return k0 * k1;
72840             }, p);
72841           };
72842
72843           zoom.scaleTo = function(selection, k, p) {
72844             zoom.transform(selection, function() {
72845               var e = extent.apply(this, arguments),
72846                   t0 = _transform,
72847                   p0 = p == null ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p,
72848                   p1 = t0.invert(p0),
72849                   k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
72850               return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
72851             }, p);
72852           };
72853
72854           zoom.translateBy = function(selection, x, y) {
72855             zoom.transform(selection, function() {
72856               return constrain(_transform.translate(
72857                 typeof x === 'function' ? x.apply(this, arguments) : x,
72858                 typeof y === 'function' ? y.apply(this, arguments) : y
72859               ), extent.apply(this, arguments), translateExtent);
72860             });
72861           };
72862
72863           zoom.translateTo = function(selection, x, y, p) {
72864             zoom.transform(selection, function() {
72865               var e = extent.apply(this, arguments),
72866                   t = _transform,
72867                   p0 = p == null ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p;
72868               return constrain(identity$2.translate(p0[0], p0[1]).scale(t.k).translate(
72869                 typeof x === 'function' ? -x.apply(this, arguments) : -x,
72870                 typeof y === 'function' ? -y.apply(this, arguments) : -y
72871               ), e, translateExtent);
72872             }, p);
72873           };
72874
72875           function scale(transform, k) {
72876             k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
72877             return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
72878           }
72879
72880           function translate(transform, p0, p1) {
72881             var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k;
72882             return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
72883           }
72884
72885           function centroid(extent) {
72886             return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
72887           }
72888
72889           function schedule(transition, transform, point) {
72890             transition
72891                 .on('start.zoom', function() { gesture(this, arguments).start(); })
72892                 .on('interrupt.zoom end.zoom', function() { gesture(this, arguments).end(); })
72893                 .tween('zoom', function() {
72894                   var that = this,
72895                       args = arguments,
72896                       g = gesture(that, args),
72897                       e = extent.apply(that, args),
72898                       p = point == null ? centroid(e) : typeof point === 'function' ? point.apply(that, args) : point,
72899                       w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
72900                       a = _transform,
72901                       b = typeof transform === 'function' ? transform.apply(that, args) : transform,
72902                       i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
72903                   return function(t) {
72904                     if (t === 1) t = b; // Avoid rounding error on end.
72905                     else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); }
72906                     g.zoom(null, t);
72907                   };
72908                 });
72909           }
72910
72911           function gesture(that, args, clean) {
72912             return (!clean && _activeGesture) || new Gesture(that, args);
72913           }
72914
72915           function Gesture(that, args) {
72916             this.that = that;
72917             this.args = args;
72918             this.active = 0;
72919             this.extent = extent.apply(that, args);
72920           }
72921
72922           Gesture.prototype = {
72923             start: function() {
72924               if (++this.active === 1) {
72925                 _activeGesture = this;
72926                 this.emit('start');
72927               }
72928               return this;
72929             },
72930             zoom: function(key, transform) {
72931               if (this.mouse && key !== 'mouse') this.mouse[1] = transform.invert(this.mouse[0]);
72932               if (this.pointer0 && key !== 'touch') this.pointer0[1] = transform.invert(this.pointer0[0]);
72933               if (this.pointer1 && key !== 'touch') this.pointer1[1] = transform.invert(this.pointer1[0]);
72934               _transform = transform;
72935               this.emit('zoom');
72936               return this;
72937             },
72938             end: function() {
72939               if (--this.active === 0) {
72940                 _activeGesture = null;
72941                 this.emit('end');
72942               }
72943               return this;
72944             },
72945             emit: function(type) {
72946               customEvent(new ZoomEvent(zoom, type, _transform), listeners.apply, listeners, [type, this.that, this.args]);
72947             }
72948           };
72949
72950           function wheeled() {
72951             if (!filter.apply(this, arguments)) return;
72952             var g = gesture(this, arguments),
72953                 t = _transform,
72954                 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
72955                 p = utilFastMouse(this)(event);
72956
72957             // If the mouse is in the same location as before, reuse it.
72958             // If there were recent wheel events, reset the wheel idle timeout.
72959             if (g.wheel) {
72960               if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
72961                 g.mouse[1] = t.invert(g.mouse[0] = p);
72962               }
72963               clearTimeout(g.wheel);
72964
72965             // Otherwise, capture the mouse point and location at the start.
72966             } else {
72967               g.mouse = [p, t.invert(p)];
72968               interrupt(this);
72969               g.start();
72970             }
72971
72972             event.preventDefault();
72973             event.stopImmediatePropagation();
72974             g.wheel = setTimeout(wheelidled, _wheelDelay);
72975             g.zoom('mouse', constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
72976
72977             function wheelidled() {
72978               g.wheel = null;
72979               g.end();
72980             }
72981           }
72982
72983           var _downPointerIDs = new Set();
72984           var _pointerLocGetter;
72985
72986           function pointerdown() {
72987             _downPointerIDs.add(event.pointerId);
72988
72989             if (!filter.apply(this, arguments)) return;
72990
72991             var g = gesture(this, arguments, _downPointerIDs.size === 1);
72992             var started;
72993
72994             event.stopImmediatePropagation();
72995             _pointerLocGetter = utilFastMouse(this);
72996             var loc = _pointerLocGetter(event);
72997             var p = [loc, _transform.invert(loc), event.pointerId];
72998             if (!g.pointer0) {
72999                g.pointer0 = p;
73000                started = true;
73001
73002             } else if (!g.pointer1 && g.pointer0[2] !== p[2]) {
73003                g.pointer1 = p;
73004             }
73005
73006             if (started) {
73007               interrupt(this);
73008               g.start();
73009             }
73010           }
73011
73012           function pointermove() {
73013             if (!_downPointerIDs.has(event.pointerId)) return;
73014
73015             if (!_activeGesture || !_pointerLocGetter) return;
73016
73017             var g = gesture(this, arguments);
73018
73019             var isPointer0 = g.pointer0 && g.pointer0[2] === event.pointerId;
73020             var isPointer1 = !isPointer0 && g.pointer1 && g.pointer1[2] === event.pointerId;
73021
73022             if ((isPointer0 || isPointer1) && 'buttons' in event && !event.buttons) {
73023               // The pointer went up without ending the gesture somehow, e.g.
73024               // a down mouse was moved off the map and released. End it here.
73025               if (g.pointer0) _downPointerIDs.delete(g.pointer0[2]);
73026               if (g.pointer1) _downPointerIDs.delete(g.pointer1[2]);
73027               g.end();
73028               return;
73029             }
73030
73031             event.preventDefault();
73032             event.stopImmediatePropagation();
73033
73034             var loc = _pointerLocGetter(event);
73035             var t, p, l;
73036
73037             if (isPointer0) g.pointer0[0] = loc;
73038             else if (isPointer1) g.pointer1[0] = loc;
73039
73040             t = _transform;
73041             if (g.pointer1) {
73042               var p0 = g.pointer0[0], l0 = g.pointer0[1],
73043                   p1 = g.pointer1[0], l1 = g.pointer1[1],
73044                   dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
73045                   dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
73046               t = scale(t, Math.sqrt(dp / dl));
73047               p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
73048               l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
73049             } else if (g.pointer0) {
73050               p = g.pointer0[0];
73051               l = g.pointer0[1];
73052             }
73053             else return;
73054             g.zoom('touch', constrain(translate(t, p, l), g.extent, translateExtent));
73055           }
73056
73057           function pointerup() {
73058             if (!_downPointerIDs.has(event.pointerId)) return;
73059
73060             _downPointerIDs.delete(event.pointerId);
73061
73062             if (!_activeGesture) return;
73063
73064             var g = gesture(this, arguments);
73065
73066             event.stopImmediatePropagation();
73067
73068             if (g.pointer0 && g.pointer0[2] === event.pointerId) delete g.pointer0;
73069             else if (g.pointer1 && g.pointer1[2] === event.pointerId) delete g.pointer1;
73070
73071             if (g.pointer1 && !g.pointer0) {
73072               g.pointer0 = g.pointer1;
73073               delete g.pointer1;
73074             }
73075             if (g.pointer0) g.pointer0[1] = _transform.invert(g.pointer0[0]);
73076             else {
73077               g.end();
73078             }
73079           }
73080
73081           zoom.wheelDelta = function(_) {
73082             return arguments.length ? (wheelDelta = utilFunctor(+_), zoom) : wheelDelta;
73083           };
73084
73085           zoom.filter = function(_) {
73086             return arguments.length ? (filter = utilFunctor(!!_), zoom) : filter;
73087           };
73088
73089           zoom.extent = function(_) {
73090             return arguments.length ? (extent = utilFunctor([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
73091           };
73092
73093           zoom.scaleExtent = function(_) {
73094             return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
73095           };
73096
73097           zoom.translateExtent = function(_) {
73098             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]]];
73099           };
73100
73101           zoom.constrain = function(_) {
73102             return arguments.length ? (constrain = _, zoom) : constrain;
73103           };
73104
73105           zoom.interpolate = function(_) {
73106             return arguments.length ? (interpolate = _, zoom) : interpolate;
73107           };
73108
73109           zoom._transform = function(_) {
73110             return arguments.length ? (_transform = _, zoom) : _transform;
73111           };
73112
73113           zoom.on = function() {
73114             var value = listeners.on.apply(listeners, arguments);
73115             return value === listeners ? zoom : value;
73116           };
73117
73118           return zoom;
73119         }
73120
73121         // A custom double-click / double-tap event detector that works on touch devices
73122         // if pointer events are supported. Falls back to default `dblclick` event.
73123         function utilDoubleUp() {
73124
73125             var dispatch$1 = dispatch('doubleUp');
73126
73127             var _maxTimespan = 500; // milliseconds
73128             var _maxDistance = 20; // web pixels; be somewhat generous to account for touch devices
73129             var _pointer; // object representing the pointer that could trigger double up
73130
73131             function pointerIsValidFor(loc) {
73132                 // second pointerup must occur within a small timeframe after the first pointerdown
73133                 return new Date().getTime() - _pointer.startTime <= _maxTimespan &&
73134                     // all pointer events must occur within a small distance of the first pointerdown
73135                     geoVecLength(_pointer.startLoc, loc) <= _maxDistance;
73136             }
73137
73138             function pointerdown() {
73139
73140                 // ignore right-click
73141                 if (event.ctrlKey || event.button === 2) return;
73142
73143                 var loc = [event.clientX, event.clientY];
73144
73145                 // Don't rely on pointerId here since it can change between pointerdown
73146                 // events on touch devices
73147                 if (_pointer && !pointerIsValidFor(loc)) {
73148                     // if this pointer is no longer valid, clear it so another can be started
73149                     _pointer = undefined;
73150                 }
73151
73152                 if (!_pointer) {
73153                     _pointer = {
73154                         startLoc: loc,
73155                         startTime: new Date().getTime(),
73156                         upCount: 0,
73157                         pointerId: event.pointerId
73158                     };
73159                 } else { // double down
73160                     _pointer.pointerId = event.pointerId;
73161                 }
73162             }
73163
73164             function pointerup() {
73165
73166                 // ignore right-click
73167                 if (event.ctrlKey || event.button === 2) return;
73168
73169                 if (!_pointer || _pointer.pointerId !== event.pointerId) return;
73170
73171                 _pointer.upCount += 1;
73172
73173                 if (_pointer.upCount === 2) { // double up!
73174                     var loc = [event.clientX, event.clientY];
73175                     if (pointerIsValidFor(loc)) {
73176                         var locInThis = utilFastMouse(this)(event);
73177                         dispatch$1.call('doubleUp', this, locInThis);
73178                     }
73179                     // clear the pointer info in any case
73180                     _pointer = undefined;
73181                 }
73182             }
73183
73184             function doubleUp(selection) {
73185                 if ('PointerEvent' in window) {
73186                     // dblclick isn't well supported on touch devices so manually use
73187                     // pointer events if they're available
73188                     selection
73189                         .on('pointerdown.doubleUp', pointerdown)
73190                         .on('pointerup.doubleUp', pointerup);
73191                 } else {
73192                     // fallback to dblclick
73193                     selection
73194                         .on('dblclick.doubleUp', function() {
73195                             dispatch$1.call('doubleUp', this, utilFastMouse(this)(event));
73196                         });
73197                 }
73198             }
73199
73200             doubleUp.off = function(selection) {
73201                 selection
73202                     .on('pointerdown.doubleUp', null)
73203                     .on('pointerup.doubleUp', null)
73204                     .on('dblclick.doubleUp', null);
73205             };
73206
73207             return utilRebind(doubleUp, dispatch$1, 'on');
73208         }
73209
73210         // constants
73211         var TILESIZE = 256;
73212         var minZoom = 2;
73213         var maxZoom = 24;
73214         var kMin = geoZoomToScale(minZoom, TILESIZE);
73215         var kMax = geoZoomToScale(maxZoom, TILESIZE);
73216
73217         function clamp(num, min, max) {
73218             return Math.max(min, Math.min(num, max));
73219         }
73220
73221
73222         function rendererMap(context) {
73223             var dispatch$1 = dispatch(
73224                 'move', 'drawn',
73225                 'crossEditableZoom', 'hitMinZoom',
73226                 'changeHighlighting', 'changeAreaFill'
73227             );
73228             var projection = context.projection;
73229             var curtainProjection = context.curtainProjection;
73230             var drawLayers;
73231             var drawPoints;
73232             var drawVertices;
73233             var drawLines;
73234             var drawAreas;
73235             var drawMidpoints;
73236             var drawLabels;
73237
73238             var _selection = select(null);
73239             var supersurface = select(null);
73240             var wrapper = select(null);
73241             var surface = select(null);
73242
73243             var _dimensions = [1, 1];
73244             var _dblClickZoomEnabled = true;
73245             var _redrawEnabled = true;
73246             var _gestureTransformStart;
73247             var _transformStart = projection.transform();
73248             var _transformLast;
73249             var _isTransformed = false;
73250             var _minzoom = 0;
73251             var _getMouseCoords;
73252             var _lastPointerEvent;
73253             var _lastWithinEditableZoom;
73254
73255             // whether a pointerdown event started the zoom
73256             var _pointerDown = false;
73257
73258             // use pointer events on supported platforms; fallback to mouse events
73259             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
73260
73261             // use pointer event interaction if supported; fallback to touch/mouse events in d3-zoom
73262             var _zoomerPannerFunction = 'PointerEvent' in window ? utilZoomPan : d3_zoom;
73263
73264             var _zoomerPanner = _zoomerPannerFunction()
73265                 .scaleExtent([kMin, kMax])
73266                 .interpolate(interpolate)
73267                 .filter(zoomEventFilter)
73268                 .on('zoom.map', zoomPan)
73269                 .on('start.map', function() {
73270                     _pointerDown = event.sourceEvent && event.sourceEvent.type === 'pointerdown';
73271                 })
73272                 .on('end.map', function() {
73273                     _pointerDown = false;
73274                 });
73275             var _doubleUpHandler = utilDoubleUp();
73276
73277             var scheduleRedraw = throttle(redraw, 750);
73278             // var isRedrawScheduled = false;
73279             // var pendingRedrawCall;
73280             // function scheduleRedraw() {
73281             //     // Only schedule the redraw if one has not already been set.
73282             //     if (isRedrawScheduled) return;
73283             //     isRedrawScheduled = true;
73284             //     var that = this;
73285             //     var args = arguments;
73286             //     pendingRedrawCall = window.requestIdleCallback(function () {
73287             //         // Reset the boolean so future redraws can be set.
73288             //         isRedrawScheduled = false;
73289             //         redraw.apply(that, args);
73290             //     }, { timeout: 1400 });
73291             // }
73292
73293             function cancelPendingRedraw() {
73294                 scheduleRedraw.cancel();
73295                 // isRedrawScheduled = false;
73296                 // window.cancelIdleCallback(pendingRedrawCall);
73297             }
73298
73299
73300             function map(selection) {
73301                 _selection = selection;
73302
73303                 context
73304                     .on('change.map', immediateRedraw);
73305
73306                 var osm = context.connection();
73307                 if (osm) {
73308                     osm.on('change.map', immediateRedraw);
73309                 }
73310
73311                 function didUndoOrRedo(targetTransform) {
73312                     var mode = context.mode().id;
73313                     if (mode !== 'browse' && mode !== 'select') return;
73314                     if (targetTransform) {
73315                         map.transformEase(targetTransform);
73316                     }
73317                 }
73318
73319                 context.history()
73320                     .on('merge.map', function() { scheduleRedraw(); })
73321                     .on('change.map', immediateRedraw)
73322                     .on('undone.map', function(stack, fromStack) {
73323                         didUndoOrRedo(fromStack.transform);
73324                     })
73325                     .on('redone.map', function(stack) {
73326                         didUndoOrRedo(stack.transform);
73327                     });
73328
73329                 context.background()
73330                     .on('change.map', immediateRedraw);
73331
73332                 context.features()
73333                     .on('redraw.map', immediateRedraw);
73334
73335                 drawLayers
73336                     .on('change.map', function() {
73337                         context.background().updateImagery();
73338                         immediateRedraw();
73339                     });
73340
73341                 selection
73342                     .on('wheel.map mousewheel.map', function() {
73343                         // disable swipe-to-navigate browser pages on trackpad/magic mouse – #5552
73344                         event.preventDefault();
73345                     })
73346                     .call(_zoomerPanner)
73347                     .call(_zoomerPanner.transform, projection.transform())
73348                     .on('dblclick.zoom', null); // override d3-zoom dblclick handling
73349
73350                 map.supersurface = supersurface = selection.append('div')
73351                     .attr('class', 'supersurface')
73352                     .call(utilSetTransform, 0, 0);
73353
73354                 // Need a wrapper div because Opera can't cope with an absolutely positioned
73355                 // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
73356                 wrapper = supersurface
73357                     .append('div')
73358                     .attr('class', 'layer layer-data');
73359
73360                 map.surface = surface = wrapper
73361                     .call(drawLayers)
73362                     .selectAll('.surface');
73363
73364                 surface
73365                     .call(drawLabels.observe)
73366                     .call(_doubleUpHandler)
73367                     .on(_pointerPrefix + 'down.zoom', function() {
73368                         _lastPointerEvent = event;
73369                         if (event.button === 2) {
73370                             event.stopPropagation();
73371                         }
73372                     }, true)
73373                     .on(_pointerPrefix + 'up.zoom', function() {
73374                         _lastPointerEvent = event;
73375                         if (resetTransform()) {
73376                             immediateRedraw();
73377                         }
73378                     })
73379                     .on(_pointerPrefix + 'move.map', function() {
73380                         _lastPointerEvent = event;
73381                     })
73382                     .on(_pointerPrefix + 'over.vertices', function() {
73383                         if (map.editableDataEnabled() && !_isTransformed) {
73384                             var hover = event.target.__data__;
73385                             surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
73386                             dispatch$1.call('drawn', this, { full: false });
73387                         }
73388                     })
73389                     .on(_pointerPrefix + 'out.vertices', function() {
73390                         if (map.editableDataEnabled() && !_isTransformed) {
73391                             var hover = event.relatedTarget && event.relatedTarget.__data__;
73392                             surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
73393                             dispatch$1.call('drawn', this, { full: false });
73394                         }
73395                     });
73396
73397                 var detected = utilDetect();
73398
73399                 // only WebKit supports gesture events
73400                 if ('GestureEvent' in window &&
73401                     // Listening for gesture events on iOS 13.4+ breaks double-tapping,
73402                     // but we only need to do this on desktop Safari anyway. – #7694
73403                     !detected.isMobileWebKit) {
73404
73405                     // Desktop Safari sends gesture events for multitouch trackpad pinches.
73406                     // We can listen for these and translate them into map zooms.
73407                     surface
73408                         .on('gesturestart.surface', function() {
73409                             event.preventDefault();
73410                             _gestureTransformStart = projection.transform();
73411                         })
73412                         .on('gesturechange.surface', gestureChange);
73413                 }
73414
73415                 // must call after surface init
73416                 updateAreaFill();
73417
73418                 _doubleUpHandler.on('doubleUp.map', function(p0) {
73419                     if (!_dblClickZoomEnabled) return;
73420
73421                     // don't zoom if targeting something other than the map itself
73422                     if (typeof event.target.__data__ === 'object' &&
73423                         // or area fills
73424                         !select(event.target).classed('fill')) return;
73425
73426                     var zoomOut = event.shiftKey;
73427
73428                     var t = projection.transform();
73429
73430                     var p1 = t.invert(p0);
73431
73432                     t = t.scale(zoomOut ? 0.5 : 2);
73433
73434                     t.x = p0[0] - p1[0] * t.k;
73435                     t.y = p0[1] - p1[1] * t.k;
73436
73437                     map.transformEase(t);
73438                 });
73439
73440                 context.on('enter.map',  function() {
73441                     if (!map.editableDataEnabled(true /* skip zoom check */)) return;
73442
73443                     // redraw immediately any objects affected by a change in selectedIDs.
73444                     var graph = context.graph();
73445                     var selectedAndParents = {};
73446                     context.selectedIDs().forEach(function(id) {
73447                         var entity = graph.hasEntity(id);
73448                         if (entity) {
73449                             selectedAndParents[entity.id] = entity;
73450                             if (entity.type === 'node') {
73451                                 graph.parentWays(entity).forEach(function(parent) {
73452                                     selectedAndParents[parent.id] = parent;
73453                                 });
73454                             }
73455                         }
73456                     });
73457                     var data = Object.values(selectedAndParents);
73458                     var filter = function(d) { return d.id in selectedAndParents; };
73459
73460                     data = context.features().filter(data, graph);
73461
73462                     surface
73463                         .call(drawVertices.drawSelected, graph, map.extent())
73464                         .call(drawLines, graph, data, filter)
73465                         .call(drawAreas, graph, data, filter)
73466                         .call(drawMidpoints, graph, data, filter, map.trimmedExtent());
73467
73468                     dispatch$1.call('drawn', this, { full: false });
73469
73470                     // redraw everything else later
73471                     scheduleRedraw();
73472                 });
73473
73474                 map.dimensions(utilGetDimensions(selection));
73475             }
73476
73477
73478             function zoomEventFilter() {
73479                 // Fix for #2151, (see also d3/d3-zoom#60, d3/d3-brush#18)
73480                 // Intercept `mousedown` and check if there is an orphaned zoom gesture.
73481                 // This can happen if a previous `mousedown` occurred without a `mouseup`.
73482                 // If we detect this, dispatch `mouseup` to complete the orphaned gesture,
73483                 // so that d3-zoom won't stop propagation of new `mousedown` events.
73484                 if (event.type === 'mousedown') {
73485                     var hasOrphan = false;
73486                     var listeners = window.__on;
73487                     for (var i = 0; i < listeners.length; i++) {
73488                         var listener = listeners[i];
73489                         if (listener.name === 'zoom' && listener.type === 'mouseup') {
73490                             hasOrphan = true;
73491                             break;
73492                         }
73493                     }
73494                     if (hasOrphan) {
73495                         var event$1 = window.CustomEvent;
73496                         if (event$1) {
73497                             event$1 = new event$1('mouseup');
73498                         } else {
73499                             event$1 = window.document.createEvent('Event');
73500                             event$1.initEvent('mouseup', false, false);
73501                         }
73502                         // Event needs to be dispatched with an event.view property.
73503                         event$1.view = window;
73504                         window.dispatchEvent(event$1);
73505                     }
73506                 }
73507
73508                 return event.button !== 2;   // ignore right clicks
73509             }
73510
73511
73512             function pxCenter() {
73513                 return [_dimensions[0] / 2, _dimensions[1] / 2];
73514             }
73515
73516
73517             function drawEditable(difference, extent) {
73518                 var mode = context.mode();
73519                 var graph = context.graph();
73520                 var features = context.features();
73521                 var all = context.history().intersects(map.extent());
73522                 var fullRedraw = false;
73523                 var data;
73524                 var set;
73525                 var filter;
73526                 var applyFeatureLayerFilters = true;
73527
73528                 if (map.isInWideSelection()) {
73529                     data = [];
73530                     utilEntityAndDeepMemberIDs(mode.selectedIDs(), context.graph()).forEach(function(id) {
73531                         var entity = context.hasEntity(id);
73532                         if (entity) data.push(entity);
73533                     });
73534                     fullRedraw = true;
73535                     filter = utilFunctor(true);
73536                     // selected features should always be visible, so we can skip filtering
73537                     applyFeatureLayerFilters = false;
73538
73539                 } else if (difference) {
73540                     var complete = difference.complete(map.extent());
73541                     data = Object.values(complete).filter(Boolean);
73542                     set = new Set(Object.keys(complete));
73543                     filter = function(d) { return set.has(d.id); };
73544                     features.clear(data);
73545
73546                 } else {
73547                     // force a full redraw if gatherStats detects that a feature
73548                     // should be auto-hidden (e.g. points or buildings)..
73549                     if (features.gatherStats(all, graph, _dimensions)) {
73550                         extent = undefined;
73551                     }
73552
73553                     if (extent) {
73554                         data = context.history().intersects(map.extent().intersection(extent));
73555                         set = new Set(data.map(function(entity) { return entity.id; }));
73556                         filter = function(d) { return set.has(d.id); };
73557
73558                     } else {
73559                         data = all;
73560                         fullRedraw = true;
73561                         filter = utilFunctor(true);
73562                     }
73563                 }
73564
73565                 if (applyFeatureLayerFilters) {
73566                     data = features.filter(data, graph);
73567                 } else {
73568                     context.features().resetStats();
73569                 }
73570
73571                 if (mode && mode.id === 'select') {
73572                     // update selected vertices - the user might have just double-clicked a way,
73573                     // creating a new vertex, triggering a partial redraw without a mode change
73574                     surface.call(drawVertices.drawSelected, graph, map.extent());
73575                 }
73576
73577                 surface
73578                     .call(drawVertices, graph, data, filter, map.extent(), fullRedraw)
73579                     .call(drawLines, graph, data, filter)
73580                     .call(drawAreas, graph, data, filter)
73581                     .call(drawMidpoints, graph, data, filter, map.trimmedExtent())
73582                     .call(drawLabels, graph, data, filter, _dimensions, fullRedraw)
73583                     .call(drawPoints, graph, data, filter);
73584
73585                 dispatch$1.call('drawn', this, {full: true});
73586             }
73587
73588             map.init = function() {
73589                 drawLayers = svgLayers(projection, context);
73590                 drawPoints = svgPoints(projection, context);
73591                 drawVertices = svgVertices(projection, context);
73592                 drawLines = svgLines(projection, context);
73593                 drawAreas = svgAreas(projection, context);
73594                 drawMidpoints = svgMidpoints(projection, context);
73595                 drawLabels = svgLabels(projection, context);
73596             };
73597
73598             function editOff() {
73599                 context.features().resetStats();
73600                 surface.selectAll('.layer-osm *').remove();
73601                 surface.selectAll('.layer-touch:not(.markers) *').remove();
73602
73603                 var allowed = {
73604                     'browse': true,
73605                     'save': true,
73606                     'select-note': true,
73607                     'select-data': true,
73608                     'select-error': true
73609                 };
73610
73611                 var mode = context.mode();
73612                 if (mode && !allowed[mode.id]) {
73613                     context.enter(modeBrowse(context));
73614                 }
73615
73616                 dispatch$1.call('drawn', this, {full: true});
73617             }
73618
73619
73620
73621
73622
73623             function gestureChange() {
73624                 // Remap Safari gesture events to wheel events - #5492
73625                 // We want these disabled most places, but enabled for zoom/unzoom on map surface
73626                 // https://developer.mozilla.org/en-US/docs/Web/API/GestureEvent
73627                 var e = event;
73628                 e.preventDefault();
73629
73630                 var props = {
73631                     deltaMode: 0,    // dummy values to ignore in zoomPan
73632                     deltaY: 1,       // dummy values to ignore in zoomPan
73633                     clientX: e.clientX,
73634                     clientY: e.clientY,
73635                     screenX: e.screenX,
73636                     screenY: e.screenY,
73637                     x: e.x,
73638                     y: e.y
73639                 };
73640
73641                 var e2 = new WheelEvent('wheel', props);
73642                 e2._scale = e.scale;         // preserve the original scale
73643                 e2._rotation = e.rotation;   // preserve the original rotation
73644
73645                 _selection.node().dispatchEvent(e2);
73646             }
73647
73648
73649             function zoomPan(manualEvent) {
73650                 var event$1 = (manualEvent || event);
73651                 var source = event$1.sourceEvent;
73652                 var eventTransform = event$1.transform;
73653                 var x = eventTransform.x;
73654                 var y = eventTransform.y;
73655                 var k = eventTransform.k;
73656
73657                 // Special handling of 'wheel' events:
73658                 // They might be triggered by the user scrolling the mouse wheel,
73659                 // or 2-finger pinch/zoom gestures, the transform may need adjustment.
73660                 if (source && source.type === 'wheel') {
73661
73662                     // assume that the gesture is already handled by pointer events
73663                     if (_pointerDown) return;
73664
73665                     var detected = utilDetect();
73666                     var dX = source.deltaX;
73667                     var dY = source.deltaY;
73668                     var x2 = x;
73669                     var y2 = y;
73670                     var k2 = k;
73671                     var t0, p0, p1;
73672
73673                     // Normalize mousewheel scroll speed (Firefox) - #3029
73674                     // If wheel delta is provided in LINE units, recalculate it in PIXEL units
73675                     // We are essentially redoing the calculations that occur here:
73676                     //   https://github.com/d3/d3-zoom/blob/78563a8348aa4133b07cac92e2595c2227ca7cd7/src/zoom.js#L203
73677                     // See this for more info:
73678                     //   https://github.com/basilfx/normalize-wheel/blob/master/src/normalizeWheel.js
73679                     if (source.deltaMode === 1 /* LINE */) {
73680                         // Convert from lines to pixels, more if the user is scrolling fast.
73681                         // (I made up the exp function to roughly match Firefox to what Chrome does)
73682                         // These numbers should be floats, because integers are treated as pan gesture below.
73683                         var lines = Math.abs(source.deltaY);
73684                         var sign = (source.deltaY > 0) ? 1 : -1;
73685                         dY = sign * clamp(
73686                             Math.exp((lines - 1) * 0.75) * 4.000244140625,
73687                             4.000244140625,    // min
73688                             350.000244140625   // max
73689                         );
73690
73691                         // On Firefox Windows and Linux we always get +/- the scroll line amount (default 3)
73692                         // There doesn't seem to be any scroll accelleration.
73693                         // This multiplier increases the speed a little bit - #5512
73694                         if (detected.os !== 'mac') {
73695                             dY *= 5;
73696                         }
73697
73698                         // recalculate x2,y2,k2
73699                         t0 = _isTransformed ? _transformLast : _transformStart;
73700                         p0 = _getMouseCoords(source);
73701                         p1 = t0.invert(p0);
73702                         k2 = t0.k * Math.pow(2, -dY / 500);
73703                         k2 = clamp(k2, kMin, kMax);
73704                         x2 = p0[0] - p1[0] * k2;
73705                         y2 = p0[1] - p1[1] * k2;
73706
73707                     // 2 finger map pinch zooming (Safari) - #5492
73708                     // These are fake `wheel` events we made from Safari `gesturechange` events..
73709                     } else if (source._scale) {
73710                         // recalculate x2,y2,k2
73711                         t0 = _gestureTransformStart;
73712                         p0 = _getMouseCoords(source);
73713                         p1 = t0.invert(p0);
73714                         k2 = t0.k * source._scale;
73715                         k2 = clamp(k2, kMin, kMax);
73716                         x2 = p0[0] - p1[0] * k2;
73717                         y2 = p0[1] - p1[1] * k2;
73718
73719                     // 2 finger map pinch zooming (all browsers except Safari) - #5492
73720                     // Pinch zooming via the `wheel` event will always have:
73721                     // - `ctrlKey = true`
73722                     // - `deltaY` is not round integer pixels (ignore `deltaX`)
73723                     } else if (source.ctrlKey && !isInteger(dY)) {
73724                         dY *= 6;   // slightly scale up whatever the browser gave us
73725
73726                         // recalculate x2,y2,k2
73727                         t0 = _isTransformed ? _transformLast : _transformStart;
73728                         p0 = _getMouseCoords(source);
73729                         p1 = t0.invert(p0);
73730                         k2 = t0.k * Math.pow(2, -dY / 500);
73731                         k2 = clamp(k2, kMin, kMax);
73732                         x2 = p0[0] - p1[0] * k2;
73733                         y2 = p0[1] - p1[1] * k2;
73734
73735                     // Trackpad scroll zooming with shift or alt/option key down
73736                     } else if ((source.altKey || source.shiftKey) && isInteger(dY)) {
73737                         // recalculate x2,y2,k2
73738                         t0 = _isTransformed ? _transformLast : _transformStart;
73739                         p0 = _getMouseCoords(source);
73740                         p1 = t0.invert(p0);
73741                         k2 = t0.k * Math.pow(2, -dY / 500);
73742                         k2 = clamp(k2, kMin, kMax);
73743                         x2 = p0[0] - p1[0] * k2;
73744                         y2 = p0[1] - p1[1] * k2;
73745
73746                     // 2 finger map panning (Mac only, all browsers) - #5492, #5512
73747                     // Panning via the `wheel` event will always have:
73748                     // - `ctrlKey = false`
73749                     // - `deltaX`,`deltaY` are round integer pixels
73750                     } else if (detected.os === 'mac' && !source.ctrlKey && isInteger(dX) && isInteger(dY)) {
73751                         p1 = projection.translate();
73752                         x2 = p1[0] - dX;
73753                         y2 = p1[1] - dY;
73754                         k2 = projection.scale();
73755                         k2 = clamp(k2, kMin, kMax);
73756                     }
73757
73758                     // something changed - replace the event transform
73759                     if (x2 !== x || y2 !== y || k2 !== k) {
73760                         x = x2;
73761                         y = y2;
73762                         k = k2;
73763                         eventTransform = identity$2.translate(x2, y2).scale(k2);
73764                         if (_zoomerPanner._transform) {
73765                             // utilZoomPan interface
73766                             _zoomerPanner._transform(eventTransform);
73767                         } else {
73768                             // d3_zoom interface
73769                             _selection.node().__zoom = eventTransform;
73770                         }
73771                     }
73772
73773                 }
73774
73775                 if (_transformStart.x === x &&
73776                     _transformStart.y === y &&
73777                     _transformStart.k === k) {
73778                     return;  // no change
73779                 }
73780
73781                 var withinEditableZoom = map.withinEditableZoom();
73782                 if (_lastWithinEditableZoom !== withinEditableZoom) {
73783                     if (_lastWithinEditableZoom !== undefined) {
73784                         // notify that the map zoomed in or out over the editable zoom threshold
73785                         dispatch$1.call('crossEditableZoom', this, withinEditableZoom);
73786                     }
73787                     _lastWithinEditableZoom = withinEditableZoom;
73788                 }
73789
73790                 if (geoScaleToZoom(k, TILESIZE) < _minzoom) {
73791                     surface.interrupt();
73792                     dispatch$1.call('hitMinZoom', this, map);
73793                     setCenterZoom(map.center(), context.minEditableZoom(), 0, true);
73794                     scheduleRedraw();
73795                     dispatch$1.call('move', this, map);
73796                     return;
73797                 }
73798
73799                 projection.transform(eventTransform);
73800
73801                 var scale = k / _transformStart.k;
73802                 var tX = (x / scale - _transformStart.x) * scale;
73803                 var tY = (y / scale - _transformStart.y) * scale;
73804
73805                 if (context.inIntro()) {
73806                     curtainProjection.transform({
73807                         x: x - tX,
73808                         y: y - tY,
73809                         k: k
73810                     });
73811                 }
73812
73813                 if (source) {
73814                     _lastPointerEvent = event$1;
73815                 }
73816                 _isTransformed = true;
73817                 _transformLast = eventTransform;
73818                 utilSetTransform(supersurface, tX, tY, scale);
73819                 scheduleRedraw();
73820
73821                 dispatch$1.call('move', this, map);
73822
73823
73824                 function isInteger(val) {
73825                     return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
73826                 }
73827             }
73828
73829
73830             function resetTransform() {
73831                 if (!_isTransformed) return false;
73832
73833                 utilSetTransform(supersurface, 0, 0);
73834                 _isTransformed = false;
73835                 if (context.inIntro()) {
73836                     curtainProjection.transform(projection.transform());
73837                 }
73838                 return true;
73839             }
73840
73841
73842             function redraw(difference, extent) {
73843                 if (surface.empty() || !_redrawEnabled) return;
73844
73845                 // If we are in the middle of a zoom/pan, we can't do differenced redraws.
73846                 // It would result in artifacts where differenced entities are redrawn with
73847                 // one transform and unchanged entities with another.
73848                 if (resetTransform()) {
73849                     difference = extent = undefined;
73850                 }
73851
73852                 var zoom = map.zoom();
73853                 var z = String(~~zoom);
73854
73855                 if (surface.attr('data-zoom') !== z) {
73856                     surface.attr('data-zoom', z);
73857                 }
73858
73859                 // class surface as `lowzoom` around z17-z18.5 (based on latitude)
73860                 var lat = map.center()[1];
73861                 var lowzoom = linear$2()
73862                     .domain([-60, 0, 60])
73863                     .range([17, 18.5, 17])
73864                     .clamp(true);
73865
73866                 surface
73867                     .classed('low-zoom', zoom <= lowzoom(lat));
73868
73869
73870                 if (!difference) {
73871                     supersurface.call(context.background());
73872                     wrapper.call(drawLayers);
73873                 }
73874
73875                 // OSM
73876                 if (map.editableDataEnabled() || map.isInWideSelection()) {
73877                     context.loadTiles(projection);
73878                     drawEditable(difference, extent);
73879                 } else {
73880                     editOff();
73881                 }
73882
73883                 _transformStart = projection.transform();
73884
73885                 return map;
73886             }
73887
73888
73889
73890             var immediateRedraw = function(difference, extent) {
73891                 if (!difference && !extent) cancelPendingRedraw();
73892                 redraw(difference, extent);
73893             };
73894
73895
73896             map.lastPointerEvent = function() {
73897                 return _lastPointerEvent;
73898             };
73899
73900
73901             map.mouse = function() {
73902                 var event$1 = _lastPointerEvent || event;
73903                 if (event$1) {
73904                     var s;
73905                     while ((s = event$1.sourceEvent)) { event$1 = s; }
73906                     return _getMouseCoords(event$1);
73907                 }
73908                 return null;
73909             };
73910
73911
73912             // returns Lng/Lat
73913             map.mouseCoordinates = function() {
73914                 var coord = map.mouse() || pxCenter();
73915                 return projection.invert(coord);
73916             };
73917
73918
73919             map.dblclickZoomEnable = function(val) {
73920                 if (!arguments.length) return _dblClickZoomEnabled;
73921                 _dblClickZoomEnabled = val;
73922                 return map;
73923             };
73924
73925
73926             map.redrawEnable = function(val) {
73927                 if (!arguments.length) return _redrawEnabled;
73928                 _redrawEnabled = val;
73929                 return map;
73930             };
73931
73932
73933             map.isTransformed = function() {
73934                 return _isTransformed;
73935             };
73936
73937
73938             function setTransform(t2, duration, force) {
73939                 var t = projection.transform();
73940                 if (!force && t2.k === t.k && t2.x === t.x && t2.y === t.y) return false;
73941
73942                 if (duration) {
73943                     _selection
73944                         .transition()
73945                         .duration(duration)
73946                         .on('start', function() { map.startEase(); })
73947                         .call(_zoomerPanner.transform, identity$2.translate(t2.x, t2.y).scale(t2.k));
73948                 } else {
73949                     projection.transform(t2);
73950                     _transformStart = t2;
73951                     _selection.call(_zoomerPanner.transform, _transformStart);
73952                 }
73953
73954                 return true;
73955             }
73956
73957
73958             function setCenterZoom(loc2, z2, duration, force) {
73959                 var c = map.center();
73960                 var z = map.zoom();
73961                 if (loc2[0] === c[0] && loc2[1] === c[1] && z2 === z && !force) return false;
73962
73963                 var proj = geoRawMercator().transform(projection.transform());  // copy projection
73964
73965                 var k2 = clamp(geoZoomToScale(z2, TILESIZE), kMin, kMax);
73966                 proj.scale(k2);
73967
73968                 var t = proj.translate();
73969                 var point = proj(loc2);
73970
73971                 var center = pxCenter();
73972                 t[0] += center[0] - point[0];
73973                 t[1] += center[1] - point[1];
73974
73975                 return setTransform(identity$2.translate(t[0], t[1]).scale(k2), duration, force);
73976             }
73977
73978
73979             map.pan = function(delta, duration) {
73980                 var t = projection.translate();
73981                 var k = projection.scale();
73982
73983                 t[0] += delta[0];
73984                 t[1] += delta[1];
73985
73986                 if (duration) {
73987                     _selection
73988                         .transition()
73989                         .duration(duration)
73990                         .on('start', function() { map.startEase(); })
73991                         .call(_zoomerPanner.transform, identity$2.translate(t[0], t[1]).scale(k));
73992                 } else {
73993                     projection.translate(t);
73994                     _transformStart = projection.transform();
73995                     _selection.call(_zoomerPanner.transform, _transformStart);
73996                     dispatch$1.call('move', this, map);
73997                     immediateRedraw();
73998                 }
73999
74000                 return map;
74001             };
74002
74003
74004             map.dimensions = function(val) {
74005                 if (!arguments.length) return _dimensions;
74006
74007                 _dimensions = val;
74008                 drawLayers.dimensions(_dimensions);
74009                 context.background().dimensions(_dimensions);
74010                 projection.clipExtent([[0, 0], _dimensions]);
74011                 _getMouseCoords = utilFastMouse(supersurface.node());
74012
74013                 scheduleRedraw();
74014                 return map;
74015             };
74016
74017
74018             function zoomIn(delta) {
74019                 setCenterZoom(map.center(), ~~map.zoom() + delta, 250, true);
74020             }
74021
74022             function zoomOut(delta) {
74023                 setCenterZoom(map.center(), ~~map.zoom() - delta, 250, true);
74024             }
74025
74026             map.zoomIn = function() { zoomIn(1); };
74027             map.zoomInFurther = function() { zoomIn(4); };
74028             map.canZoomIn = function() { return map.zoom() < maxZoom; };
74029
74030             map.zoomOut = function() { zoomOut(1); };
74031             map.zoomOutFurther = function() { zoomOut(4); };
74032             map.canZoomOut = function() { return map.zoom() > minZoom; };
74033
74034             map.center = function(loc2) {
74035                 if (!arguments.length) {
74036                     return projection.invert(pxCenter());
74037                 }
74038
74039                 if (setCenterZoom(loc2, map.zoom())) {
74040                     dispatch$1.call('move', this, map);
74041                 }
74042
74043                 scheduleRedraw();
74044                 return map;
74045             };
74046
74047             map.unobscuredCenterZoomEase = function(loc, zoom) {
74048                 var offset = map.unobscuredOffsetPx();
74049
74050                 var proj = geoRawMercator().transform(projection.transform());  // copy projection
74051                 // use the target zoom to calculate the offset center
74052                 proj.scale(geoZoomToScale(zoom, TILESIZE));
74053
74054                 var locPx = proj(loc);
74055                 var offsetLocPx = [locPx[0] + offset[0], locPx[1] + offset[1]];
74056                 var offsetLoc = proj.invert(offsetLocPx);
74057
74058                 map.centerZoomEase(offsetLoc, zoom);
74059             };
74060
74061             map.unobscuredOffsetPx = function() {
74062                 var openPane = context.container().select('.map-panes .map-pane.shown');
74063                 if (!openPane.empty()) {
74064                     return [openPane.node().offsetWidth/2, 0];
74065                 }
74066                 return [0, 0];
74067             };
74068
74069             map.zoom = function(z2) {
74070                 if (!arguments.length) {
74071                     return Math.max(geoScaleToZoom(projection.scale(), TILESIZE), 0);
74072                 }
74073
74074                 if (z2 < _minzoom) {
74075                     surface.interrupt();
74076                     dispatch$1.call('hitMinZoom', this, map);
74077                     z2 = context.minEditableZoom();
74078                 }
74079
74080                 if (setCenterZoom(map.center(), z2)) {
74081                     dispatch$1.call('move', this, map);
74082                 }
74083
74084                 scheduleRedraw();
74085                 return map;
74086             };
74087
74088
74089             map.centerZoom = function(loc2, z2) {
74090                 if (setCenterZoom(loc2, z2)) {
74091                     dispatch$1.call('move', this, map);
74092                 }
74093
74094                 scheduleRedraw();
74095                 return map;
74096             };
74097
74098
74099             map.zoomTo = function(entity) {
74100                 var extent = entity.extent(context.graph());
74101                 if (!isFinite(extent.area())) return map;
74102
74103                 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
74104                 return map.centerZoom(extent.center(), z2);
74105             };
74106
74107
74108             map.centerEase = function(loc2, duration) {
74109                 duration = duration || 250;
74110                 setCenterZoom(loc2, map.zoom(), duration);
74111                 return map;
74112             };
74113
74114
74115             map.zoomEase = function(z2, duration) {
74116                 duration = duration || 250;
74117                 setCenterZoom(map.center(), z2, duration, false);
74118                 return map;
74119             };
74120
74121
74122             map.centerZoomEase = function(loc2, z2, duration) {
74123                 duration = duration || 250;
74124                 setCenterZoom(loc2, z2, duration, false);
74125                 return map;
74126             };
74127
74128
74129             map.transformEase = function(t2, duration) {
74130                 duration = duration || 250;
74131                 setTransform(t2, duration, false /* don't force */);
74132                 return map;
74133             };
74134
74135
74136             map.zoomToEase = function(obj, duration) {
74137                 var extent;
74138                 if (Array.isArray(obj)) {
74139                     obj.forEach(function(entity) {
74140                         var entityExtent = entity.extent(context.graph());
74141                         if (!extent) {
74142                             extent = entityExtent;
74143                         } else {
74144                             extent = extent.extend(entityExtent);
74145                         }
74146                     });
74147                 } else {
74148                     extent = obj.extent(context.graph());
74149                 }
74150                 if (!isFinite(extent.area())) return map;
74151
74152                 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
74153                 return map.centerZoomEase(extent.center(), z2, duration);
74154             };
74155
74156
74157             map.startEase = function() {
74158                 utilBindOnce(surface, _pointerPrefix + 'down.ease', function() {
74159                     map.cancelEase();
74160                 });
74161                 return map;
74162             };
74163
74164
74165             map.cancelEase = function() {
74166                 _selection.interrupt();
74167                 return map;
74168             };
74169
74170
74171             map.extent = function(val) {
74172                 if (!arguments.length) {
74173                     return new geoExtent(
74174                         projection.invert([0, _dimensions[1]]),
74175                         projection.invert([_dimensions[0], 0])
74176                     );
74177                 } else {
74178                     var extent = geoExtent(val);
74179                     map.centerZoom(extent.center(), map.extentZoom(extent));
74180                 }
74181             };
74182
74183
74184             map.trimmedExtent = function(val) {
74185                 if (!arguments.length) {
74186                     var headerY = 71;
74187                     var footerY = 30;
74188                     var pad = 10;
74189                     return new geoExtent(
74190                         projection.invert([pad, _dimensions[1] - footerY - pad]),
74191                         projection.invert([_dimensions[0] - pad, headerY + pad])
74192                     );
74193                 } else {
74194                     var extent = geoExtent(val);
74195                     map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
74196                 }
74197             };
74198
74199
74200             function calcExtentZoom(extent, dim) {
74201                 var tl = projection([extent[0][0], extent[1][1]]);
74202                 var br = projection([extent[1][0], extent[0][1]]);
74203
74204                 // Calculate maximum zoom that fits extent
74205                 var hFactor = (br[0] - tl[0]) / dim[0];
74206                 var vFactor = (br[1] - tl[1]) / dim[1];
74207                 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
74208                 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
74209                 var newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
74210
74211                 return newZoom;
74212             }
74213
74214
74215             map.extentZoom = function(val) {
74216                 return calcExtentZoom(geoExtent(val), _dimensions);
74217             };
74218
74219
74220             map.trimmedExtentZoom = function(val) {
74221                 var trimY = 120;
74222                 var trimX = 40;
74223                 var trimmed = [_dimensions[0] - trimX, _dimensions[1] - trimY];
74224                 return calcExtentZoom(geoExtent(val), trimmed);
74225             };
74226
74227
74228             map.withinEditableZoom = function() {
74229                 return map.zoom() >= context.minEditableZoom();
74230             };
74231
74232
74233             map.isInWideSelection = function() {
74234                 return !map.withinEditableZoom() && context.selectedIDs().length;
74235             };
74236
74237
74238             map.editableDataEnabled = function(skipZoomCheck) {
74239
74240                 var layer = context.layers().layer('osm');
74241                 if (!layer || !layer.enabled()) return false;
74242
74243                 return skipZoomCheck || map.withinEditableZoom();
74244             };
74245
74246
74247             map.notesEditable = function() {
74248                 var layer = context.layers().layer('notes');
74249                 if (!layer || !layer.enabled()) return false;
74250
74251                 return map.withinEditableZoom();
74252             };
74253
74254
74255             map.minzoom = function(val) {
74256                 if (!arguments.length) return _minzoom;
74257                 _minzoom = val;
74258                 return map;
74259             };
74260
74261
74262             map.toggleHighlightEdited = function() {
74263                 surface.classed('highlight-edited', !surface.classed('highlight-edited'));
74264                 map.pan([0,0]);  // trigger a redraw
74265                 dispatch$1.call('changeHighlighting', this);
74266             };
74267
74268
74269             map.areaFillOptions = ['wireframe', 'partial', 'full'];
74270
74271             map.activeAreaFill = function(val) {
74272                 if (!arguments.length) return corePreferences('area-fill') || 'partial';
74273
74274                 corePreferences('area-fill', val);
74275                 if (val !== 'wireframe') {
74276                     corePreferences('area-fill-toggle', val);
74277                 }
74278                 updateAreaFill();
74279                 map.pan([0,0]);  // trigger a redraw
74280                 dispatch$1.call('changeAreaFill', this);
74281                 return map;
74282             };
74283
74284             map.toggleWireframe = function() {
74285
74286                 var activeFill = map.activeAreaFill();
74287
74288                 if (activeFill === 'wireframe') {
74289                     activeFill = corePreferences('area-fill-toggle') || 'partial';
74290                 } else {
74291                     activeFill = 'wireframe';
74292                 }
74293
74294                 map.activeAreaFill(activeFill);
74295             };
74296
74297             function updateAreaFill() {
74298                 var activeFill = map.activeAreaFill();
74299                 map.areaFillOptions.forEach(function(opt) {
74300                     surface.classed('fill-' + opt, Boolean(opt === activeFill));
74301                 });
74302             }
74303
74304
74305             map.layers = () => drawLayers;
74306
74307
74308             map.doubleUpHandler = function() {
74309                 return _doubleUpHandler;
74310             };
74311
74312
74313             return utilRebind(map, dispatch$1, 'on');
74314         }
74315
74316         function rendererPhotos(context) {
74317             var dispatch$1 = dispatch('change');
74318             var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'openstreetcam'];
74319             var _allPhotoTypes = ['flat', 'panoramic'];
74320             var _shownPhotoTypes = _allPhotoTypes.slice();   // shallow copy
74321
74322             function photos() {}
74323
74324             function updateStorage() {
74325                 if (window.mocha) return;
74326
74327                 var hash = utilStringQs(window.location.hash);
74328                 var enabled = context.layers().all().filter(function(d) {
74329                     return _layerIDs.indexOf(d.id) !== -1 && d.layer && d.layer.supported() && d.layer.enabled();
74330                 }).map(function(d) {
74331                     return d.id;
74332                 });
74333                 if (enabled.length) {
74334                     hash.photo_overlay = enabled.join(',');
74335                 } else {
74336                     delete hash.photo_overlay;
74337                 }
74338                 window.location.replace('#' + utilQsString(hash, true));
74339             }
74340
74341             photos.overlayLayerIDs = function() {
74342                 return _layerIDs;
74343             };
74344
74345             photos.allPhotoTypes = function() {
74346                 return _allPhotoTypes;
74347             };
74348
74349             function showsLayer(id) {
74350                 var layer = context.layers().layer(id);
74351                 return layer && layer.supported() && layer.enabled();
74352             }
74353
74354             photos.shouldFilterByPhotoType = function() {
74355                 return showsLayer('mapillary') ||
74356                     (showsLayer('streetside') && showsLayer('openstreetcam'));
74357             };
74358
74359             photos.showsPhotoType = function(val) {
74360                 if (!photos.shouldFilterByPhotoType()) return true;
74361
74362                 return _shownPhotoTypes.indexOf(val) !== -1;
74363             };
74364
74365             photos.showsFlat = function() {
74366                 return photos.showsPhotoType('flat');
74367             };
74368
74369             photos.showsPanoramic = function() {
74370                 return photos.showsPhotoType('panoramic');
74371             };
74372
74373             photos.togglePhotoType = function(val) {
74374                 var index = _shownPhotoTypes.indexOf(val);
74375                 if (index !== -1) {
74376                     _shownPhotoTypes.splice(index, 1);
74377                 } else {
74378                     _shownPhotoTypes.push(val);
74379                 }
74380                 dispatch$1.call('change', this);
74381                 return photos;
74382             };
74383
74384             photos.init = function() {
74385                 var hash = utilStringQs(window.location.hash);
74386                 if (hash.photo_overlay) {
74387                     var hashOverlayIDs = hash.photo_overlay.replace(/;/g, ',').split(',');
74388                     hashOverlayIDs.forEach(function(id) {
74389                         var layer = context.layers().layer(id);
74390                         if (layer) layer.enabled(true);
74391                     });
74392                 }
74393
74394                 context.layers().on('change.rendererPhotos', updateStorage);
74395             };
74396
74397             return utilRebind(photos, dispatch$1, 'on');
74398         }
74399
74400         function uiAccount(context) {
74401             var osm = context.connection();
74402
74403
74404             function update(selection) {
74405                 if (!osm) return;
74406
74407                 if (!osm.authenticated()) {
74408                     selection.selectAll('.userLink, .logoutLink')
74409                         .classed('hide', true);
74410                     return;
74411                 }
74412
74413                 osm.userDetails(function(err, details) {
74414                     var userLink = selection.select('.userLink'),
74415                         logoutLink = selection.select('.logoutLink');
74416
74417                     userLink.html('');
74418                     logoutLink.html('');
74419
74420                     if (err || !details) return;
74421
74422                     selection.selectAll('.userLink, .logoutLink')
74423                         .classed('hide', false);
74424
74425                     // Link
74426                     userLink.append('a')
74427                         .attr('href', osm.userURL(details.display_name))
74428                         .attr('target', '_blank');
74429
74430                     // Add thumbnail or dont
74431                     if (details.image_url) {
74432                         userLink.append('img')
74433                             .attr('class', 'icon pre-text user-icon')
74434                             .attr('src', details.image_url);
74435                     } else {
74436                         userLink
74437                             .call(svgIcon('#iD-icon-avatar', 'pre-text light'));
74438                     }
74439
74440                     // Add user name
74441                     userLink.append('span')
74442                         .attr('class', 'label')
74443                         .text(details.display_name);
74444
74445                     logoutLink.append('a')
74446                         .attr('class', 'logout')
74447                         .attr('href', '#')
74448                         .text(_t('logout'))
74449                         .on('click.logout', function() {
74450                             event.preventDefault();
74451                             osm.logout();
74452                         });
74453                 });
74454             }
74455
74456
74457             return function(selection) {
74458                 selection.append('li')
74459                     .attr('class', 'logoutLink')
74460                     .classed('hide', true);
74461
74462                 selection.append('li')
74463                     .attr('class', 'userLink')
74464                     .classed('hide', true);
74465
74466                 if (osm) {
74467                     osm.on('change.account', function() { update(selection); });
74468                     update(selection);
74469                 }
74470             };
74471         }
74472
74473         function uiAttribution(context) {
74474           let _selection = select(null);
74475
74476
74477           function render(selection, data, klass) {
74478             let div = selection.selectAll(`.${klass}`)
74479               .data([0]);
74480
74481             div = div.enter()
74482               .append('div')
74483               .attr('class', klass)
74484               .merge(div);
74485
74486
74487             let attributions = div.selectAll('.attribution')
74488               .data(data, d => d.id);
74489
74490             attributions.exit()
74491               .remove();
74492
74493             attributions = attributions.enter()
74494               .append('span')
74495               .attr('class', 'attribution')
74496               .each((d, i, nodes) => {
74497                 let attribution = select(nodes[i]);
74498
74499                 if (d.terms_html) {
74500                   attribution.html(d.terms_html);
74501                   return;
74502                 }
74503
74504                 if (d.terms_url) {
74505                   attribution = attribution
74506                     .append('a')
74507                     .attr('href', d.terms_url)
74508                     .attr('target', '_blank');
74509                 }
74510
74511                 const sourceID = d.id.replace(/\./g, '<TX_DOT>');
74512                 const terms_text = _t(`imagery.${sourceID}.attribution.text`,
74513                   { default: d.terms_text || d.id || d.name() }
74514                 );
74515
74516                 if (d.icon && !d.overlay) {
74517                   attribution
74518                     .append('img')
74519                     .attr('class', 'source-image')
74520                     .attr('src', d.icon);
74521                 }
74522
74523                 attribution
74524                   .append('span')
74525                   .attr('class', 'attribution-text')
74526                   .text(terms_text);
74527               })
74528               .merge(attributions);
74529
74530
74531             let copyright = attributions.selectAll('.copyright-notice')
74532               .data(d => {
74533                 let notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
74534                 return notice ? [notice] : [];
74535               });
74536
74537             copyright.exit()
74538               .remove();
74539
74540             copyright = copyright.enter()
74541               .append('span')
74542               .attr('class', 'copyright-notice')
74543               .merge(copyright);
74544
74545             copyright
74546               .text(String);
74547           }
74548
74549
74550           function update() {
74551             let baselayer = context.background().baseLayerSource();
74552             _selection
74553               .call(render, (baselayer ? [baselayer] : []), 'base-layer-attribution');
74554
74555             const z = context.map().zoom();
74556             let overlays = context.background().overlayLayerSources() || [];
74557             _selection
74558               .call(render, overlays.filter(s => s.validZoom(z)), 'overlay-layer-attribution');
74559           }
74560
74561
74562           return function(selection) {
74563             _selection = selection;
74564
74565             context.background()
74566               .on('change.attribution', update);
74567
74568             context.map()
74569               .on('move.attribution', throttle(update, 400, { leading: false }));
74570
74571             update();
74572           };
74573         }
74574
74575         function uiContributors(context) {
74576             var osm = context.connection(),
74577                 debouncedUpdate = debounce(function() { update(); }, 1000),
74578                 limit = 4,
74579                 hidden = false,
74580                 wrap = select(null);
74581
74582
74583             function update() {
74584                 if (!osm) return;
74585
74586                 var users = {},
74587                     entities = context.history().intersects(context.map().extent());
74588
74589                 entities.forEach(function(entity) {
74590                     if (entity && entity.user) users[entity.user] = true;
74591                 });
74592
74593                 var u = Object.keys(users),
74594                     subset = u.slice(0, u.length > limit ? limit - 1 : limit);
74595
74596                 wrap.html('')
74597                     .call(svgIcon('#iD-icon-nearby', 'pre-text light'));
74598
74599                 var userList = select(document.createElement('span'));
74600
74601                 userList.selectAll()
74602                     .data(subset)
74603                     .enter()
74604                     .append('a')
74605                     .attr('class', 'user-link')
74606                     .attr('href', function(d) { return osm.userURL(d); })
74607                     .attr('target', '_blank')
74608                     .text(String);
74609
74610                 if (u.length > limit) {
74611                     var count = select(document.createElement('span'));
74612
74613                     count.append('a')
74614                         .attr('target', '_blank')
74615                         .attr('href', function() {
74616                             return osm.changesetsURL(context.map().center(), context.map().zoom());
74617                         })
74618                         .text(u.length - limit + 1);
74619
74620                     wrap.append('span')
74621                         .html(_t('contributors.truncated_list', { users: userList.html(), count: count.html() }));
74622
74623                 } else {
74624                     wrap.append('span')
74625                         .html(_t('contributors.list', { users: userList.html() }));
74626                 }
74627
74628                 if (!u.length) {
74629                     hidden = true;
74630                     wrap
74631                         .transition()
74632                         .style('opacity', 0);
74633
74634                 } else if (hidden) {
74635                     wrap
74636                         .transition()
74637                         .style('opacity', 1);
74638                 }
74639             }
74640
74641
74642             return function(selection) {
74643                 if (!osm) return;
74644                 wrap = selection;
74645                 update();
74646
74647                 osm.on('loaded.contributors', debouncedUpdate);
74648                 context.map().on('move.contributors', debouncedUpdate);
74649             };
74650         }
74651
74652         var _popoverID = 0;
74653
74654         function uiPopover(klass) {
74655             var _id = _popoverID++;
74656             var _anchorSelection = select(null);
74657             var popover = function(selection) {
74658                 _anchorSelection = selection;
74659                 selection.each(setup);
74660             };
74661             var _animation = utilFunctor(false);
74662             var _placement = utilFunctor('top'); // top, bottom, left, right
74663             var _alignment = utilFunctor('center');  // leading, center, trailing
74664             var _scrollContainer = utilFunctor(select(null));
74665             var _content;
74666             var _displayType = utilFunctor('');
74667             var _hasArrow = utilFunctor(true);
74668
74669             // use pointer events on supported platforms; fallback to mouse events
74670             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
74671
74672             popover.displayType = function(val) {
74673                 if (arguments.length) {
74674                     _displayType = utilFunctor(val);
74675                     return popover;
74676                 } else {
74677                     return _displayType;
74678                 }
74679             };
74680
74681             popover.hasArrow = function(val) {
74682                 if (arguments.length) {
74683                     _hasArrow = utilFunctor(val);
74684                     return popover;
74685                 } else {
74686                     return _hasArrow;
74687                 }
74688             };
74689
74690             popover.placement = function(val) {
74691                 if (arguments.length) {
74692                     _placement = utilFunctor(val);
74693                     return popover;
74694                 } else {
74695                     return _placement;
74696                 }
74697             };
74698
74699             popover.alignment = function(val) {
74700                 if (arguments.length) {
74701                     _alignment = utilFunctor(val);
74702                     return popover;
74703                 } else {
74704                     return _alignment;
74705                 }
74706             };
74707
74708             popover.scrollContainer = function(val) {
74709                 if (arguments.length) {
74710                     _scrollContainer = utilFunctor(val);
74711                     return popover;
74712                 } else {
74713                     return _scrollContainer;
74714                 }
74715             };
74716
74717             popover.content = function(val) {
74718                 if (arguments.length) {
74719                     _content = val;
74720                     return popover;
74721                 } else {
74722                     return _content;
74723                 }
74724             };
74725
74726             popover.isShown = function() {
74727                 var popoverSelection = _anchorSelection.select('.popover-' + _id);
74728                 return !popoverSelection.empty() && popoverSelection.classed('in');
74729             };
74730
74731             popover.show = function() {
74732                 _anchorSelection.each(show);
74733             };
74734
74735             popover.updateContent = function() {
74736                 _anchorSelection.each(updateContent);
74737             };
74738
74739             popover.hide = function() {
74740                 _anchorSelection.each(hide);
74741             };
74742
74743             popover.toggle = function() {
74744                 _anchorSelection.each(toggle);
74745             };
74746
74747             popover.destroy = function(selection, selector) {
74748                 // by default, just destroy the current popover
74749                 selector = selector || '.popover-' + _id;
74750
74751                 selection
74752                     .on(_pointerPrefix + 'enter.popover', null)
74753                     .on(_pointerPrefix + 'leave.popover', null)
74754                     .on(_pointerPrefix + 'up.popover', null)
74755                     .on(_pointerPrefix + 'down.popover', null)
74756                     .on('click.popover', null)
74757                     .attr('title', function() {
74758                         return this.getAttribute('data-original-title') || this.getAttribute('title');
74759                     })
74760                     .attr('data-original-title', null)
74761                     .selectAll(selector)
74762                     .remove();
74763             };
74764
74765
74766             popover.destroyAny = function(selection) {
74767                 selection.call(popover.destroy, '.popover');
74768             };
74769
74770             function setup() {
74771                 var anchor = select(this);
74772                 var animate = _animation.apply(this, arguments);
74773                 var popoverSelection = anchor.selectAll('.popover-' + _id)
74774                     .data([0]);
74775
74776
74777                 var enter = popoverSelection.enter()
74778                     .append('div')
74779                     .attr('class', 'popover popover-' + _id + ' ' + (klass ? klass : ''))
74780                     .classed('arrowed', _hasArrow.apply(this, arguments));
74781
74782                 enter
74783                     .append('div')
74784                     .attr('class', 'popover-arrow');
74785
74786                 enter
74787                     .append('div')
74788                     .attr('class', 'popover-inner');
74789
74790                 popoverSelection = enter
74791                     .merge(popoverSelection);
74792
74793                 if (animate) {
74794                     popoverSelection.classed('fade', true);
74795                 }
74796
74797                 var display = _displayType.apply(this, arguments);
74798
74799                 if (display === 'hover') {
74800                     var _lastNonMouseEnterTime;
74801                     anchor.on(_pointerPrefix + 'enter.popover', function() {
74802
74803                         if (event.pointerType) {
74804                             if (event.pointerType !== 'mouse') {
74805                                 _lastNonMouseEnterTime = event.timeStamp;
74806                                 // only allow hover behavior for mouse input
74807                                 return;
74808                             } else if (_lastNonMouseEnterTime &&
74809                                 event.timeStamp - _lastNonMouseEnterTime < 1500) {
74810                                 // HACK: iOS 13.4 sends an erroneous `mouse` type pointerenter
74811                                 // event for non-mouse interactions right after sending
74812                                 // the correct type pointerenter event. Workaround by discarding
74813                                 // any mouse event that occurs immediately after a non-mouse event.
74814                                 return;
74815                             }
74816                         }
74817
74818                         // don't show if buttons are pressed, e.g. during click and drag of map
74819                         if (event.buttons !== 0) return;
74820
74821                         show.apply(this, arguments);
74822                     });
74823                     anchor.on(_pointerPrefix + 'leave.popover', function() {
74824                         hide.apply(this, arguments);
74825                     });
74826
74827                 } else if (display === 'clickFocus') {
74828                     anchor
74829                         .on(_pointerPrefix + 'down.popover', function() {
74830                             event.preventDefault();
74831                             event.stopPropagation();
74832                         })
74833                         .on(_pointerPrefix + 'up.popover', function() {
74834                             event.preventDefault();
74835                             event.stopPropagation();
74836                         })
74837                         .on('click.popover', toggle);
74838
74839                     popoverSelection
74840                         .attr('tabindex', 0)
74841                         .on('blur.popover', function() {
74842                             anchor.each(function() {
74843                                 hide.apply(this, arguments);
74844                             });
74845                         });
74846                 }
74847             }
74848
74849
74850             function show() {
74851                 var anchor = select(this);
74852                 var popoverSelection = anchor.selectAll('.popover-' + _id);
74853
74854                 if (popoverSelection.empty()) {
74855                     // popover was removed somehow, put it back
74856                     anchor.call(popover.destroy);
74857                     anchor.each(setup);
74858                     popoverSelection = anchor.selectAll('.popover-' + _id);
74859                 }
74860
74861                 popoverSelection.classed('in', true);
74862
74863                 var displayType = _displayType.apply(this, arguments);
74864                 if (displayType === 'clickFocus') {
74865                     anchor.classed('active', true);
74866                     popoverSelection.node().focus();
74867                 }
74868
74869                 anchor.each(updateContent);
74870             }
74871
74872             function updateContent() {
74873                 var anchor = select(this);
74874
74875                 if (_content) {
74876                     anchor.selectAll('.popover-' + _id + ' > .popover-inner')
74877                         .call(_content.apply(this, arguments));
74878                 }
74879
74880                 updatePosition.apply(this, arguments);
74881                 // hack: update multiple times to fix instances where the absolute offset is
74882                 // set before the dynamic popover size is calculated by the browser
74883                 updatePosition.apply(this, arguments);
74884                 updatePosition.apply(this, arguments);
74885             }
74886
74887
74888             function updatePosition() {
74889
74890                 var anchor = select(this);
74891                 var popoverSelection = anchor.selectAll('.popover-' + _id);
74892
74893                 var scrollContainer = _scrollContainer && _scrollContainer.apply(this, arguments);
74894                 var scrollNode = scrollContainer && !scrollContainer.empty() && scrollContainer.node();
74895                 var scrollLeft = scrollNode ? scrollNode.scrollLeft : 0;
74896                 var scrollTop = scrollNode ? scrollNode.scrollTop : 0;
74897
74898                 var placement = _placement.apply(this, arguments);
74899                 popoverSelection
74900                     .classed('left', false)
74901                     .classed('right', false)
74902                     .classed('top', false)
74903                     .classed('bottom', false)
74904                     .classed(placement, true);
74905
74906                 var alignment = _alignment.apply(this, arguments);
74907                 var alignFactor = 0.5;
74908                 if (alignment === 'leading') {
74909                     alignFactor = 0;
74910                 } else if (alignment === 'trailing') {
74911                     alignFactor = 1;
74912                 }
74913                 var anchorFrame = getFrame(anchor.node());
74914                 var popoverFrame = getFrame(popoverSelection.node());
74915                 var position;
74916
74917                 switch (placement) {
74918                     case 'top':
74919                     position = {
74920                         x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
74921                         y: anchorFrame.y - popoverFrame.h
74922                     };
74923                     break;
74924                     case 'bottom':
74925                     position = {
74926                         x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
74927                         y: anchorFrame.y + anchorFrame.h
74928                     };
74929                     break;
74930                     case 'left':
74931                     position = {
74932                         x: anchorFrame.x - popoverFrame.w,
74933                         y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
74934                     };
74935                     break;
74936                     case 'right':
74937                     position = {
74938                         x: anchorFrame.x + anchorFrame.w,
74939                         y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
74940                     };
74941                     break;
74942                 }
74943
74944                 if (position) {
74945
74946                     if (scrollNode && (placement === 'top' || placement === 'bottom')) {
74947
74948                         var initialPosX = position.x;
74949
74950                         if (position.x + popoverFrame.w > scrollNode.offsetWidth - 10) {
74951                             position.x = scrollNode.offsetWidth - 10 - popoverFrame.w;
74952                         } else if (position.x < 10) {
74953                             position.x = 10;
74954                         }
74955
74956                         var arrow = anchor.selectAll('.popover-' + _id + ' > .popover-arrow');
74957                         // keep the arrow centered on the button, or as close as possible
74958                         var arrowPosX = Math.min(Math.max(popoverFrame.w / 2 - (position.x - initialPosX), 10), popoverFrame.w - 10);
74959                         arrow.style('left', ~~arrowPosX + 'px');
74960                     }
74961
74962                     popoverSelection.style('left', ~~position.x + 'px').style('top', ~~position.y + 'px');
74963                 } else {
74964                     popoverSelection.style('left', null).style('top', null);
74965                 }
74966
74967                 function getFrame(node) {
74968                     var positionStyle = select(node).style('position');
74969                     if (positionStyle === 'absolute' || positionStyle === 'static') {
74970                         return {
74971                             x: node.offsetLeft - scrollLeft,
74972                             y: node.offsetTop - scrollTop,
74973                             w: node.offsetWidth,
74974                             h: node.offsetHeight
74975                         };
74976                     } else {
74977                         return {
74978                             x: 0,
74979                             y: 0,
74980                             w: node.offsetWidth,
74981                             h: node.offsetHeight
74982                         };
74983                     }
74984                 }
74985             }
74986
74987
74988             function hide() {
74989                 var anchor = select(this);
74990                 if (_displayType.apply(this, arguments) === 'clickFocus') {
74991                     anchor.classed('active', false);
74992                 }
74993                 anchor.selectAll('.popover-' + _id).classed('in', false);
74994             }
74995
74996
74997             function toggle() {
74998                 if (select(this).select('.popover-' + _id).classed('in')) {
74999                     hide.apply(this, arguments);
75000                 } else {
75001                     show.apply(this, arguments);
75002                 }
75003             }
75004
75005
75006             return popover;
75007         }
75008
75009         function uiTooltip(klass) {
75010
75011             var tooltip = uiPopover((klass || '') + ' tooltip')
75012                 .displayType('hover');
75013
75014             var _title = function() {
75015                 var title = this.getAttribute('data-original-title');
75016                 if (title) {
75017                     return title;
75018                 } else {
75019                     title = this.getAttribute('title');
75020                     this.removeAttribute('title');
75021                     this.setAttribute('data-original-title', title);
75022                 }
75023                 return title;
75024             };
75025
75026             var _heading = utilFunctor(null);
75027             var _keys = utilFunctor(null);
75028
75029             tooltip.title = function(val) {
75030                 if (!arguments.length) return _title;
75031                 _title = utilFunctor(val);
75032                 return tooltip;
75033             };
75034
75035             tooltip.heading = function(val) {
75036                 if (!arguments.length) return _heading;
75037                 _heading = utilFunctor(val);
75038                 return tooltip;
75039             };
75040
75041             tooltip.keys = function(val) {
75042                 if (!arguments.length) return _keys;
75043                 _keys = utilFunctor(val);
75044                 return tooltip;
75045             };
75046
75047             tooltip.content(function() {
75048                 var heading = _heading.apply(this, arguments);
75049                 var text = _title.apply(this, arguments);
75050                 var keys = _keys.apply(this, arguments);
75051
75052                 return function(selection) {
75053
75054                     var headingSelect = selection
75055                         .selectAll('.tooltip-heading')
75056                         .data(heading ? [heading] :[]);
75057
75058                     headingSelect.exit()
75059                         .remove();
75060
75061                     headingSelect.enter()
75062                         .append('div')
75063                         .attr('class', 'tooltip-heading')
75064                         .merge(headingSelect)
75065                         .html(heading);
75066
75067                     var textSelect = selection
75068                         .selectAll('.tooltip-text')
75069                         .data(text ? [text] :[]);
75070
75071                     textSelect.exit()
75072                         .remove();
75073
75074                     textSelect.enter()
75075                         .append('div')
75076                         .attr('class', 'tooltip-text')
75077                         .merge(textSelect)
75078                         .html(text);
75079
75080                     var keyhintWrap = selection
75081                         .selectAll('.keyhint-wrap')
75082                         .data(keys && keys.length ? [0] : []);
75083
75084                     keyhintWrap.exit()
75085                         .remove();
75086
75087                     var keyhintWrapEnter = keyhintWrap.enter()
75088                         .append('div')
75089                         .attr('class', 'keyhint-wrap');
75090
75091                     keyhintWrapEnter
75092                         .append('span')
75093                         .html(_t('tooltip_keyhint'));
75094
75095                     keyhintWrap = keyhintWrapEnter.merge(keyhintWrap);
75096
75097                     keyhintWrap.selectAll('kbd.shortcut')
75098                         .data(keys && keys.length ? keys : [])
75099                         .enter()
75100                         .append('kbd')
75101                         .attr('class', 'shortcut')
75102                         .html(function(d) {
75103                             return d;
75104                         });
75105                 };
75106             });
75107
75108             return tooltip;
75109         }
75110
75111         function uiEditMenu(context) {
75112             var dispatch$1 = dispatch('toggled');
75113
75114             var _menu = select(null);
75115             var _operations = [];
75116             // the position the menu should be displayed relative to
75117             var _anchorLoc = [0, 0];
75118             var _anchorLocLonLat = [0, 0];
75119             // a string indicating how the menu was opened
75120             var _triggerType = '';
75121
75122             var _vpTopMargin = 85; // viewport top margin
75123             var _vpBottomMargin = 45; // viewport bottom margin
75124             var _vpSideMargin = 35;   // viewport side margin
75125
75126             var _menuTop = false;
75127             var _menuHeight;
75128             var _menuWidth;
75129
75130             // hardcode these values to make menu positioning easier
75131             var _verticalPadding = 4;
75132
75133             // see also `.edit-menu .tooltip` CSS; include margin
75134             var _tooltipWidth = 210;
75135
75136             // offset the menu slightly from the target location
75137             var _menuSideMargin = 10;
75138
75139             var _tooltips = [];
75140
75141             var editMenu = function(selection) {
75142
75143                 var isTouchMenu = _triggerType.includes('touch') || _triggerType.includes('pen');
75144
75145                 var ops = _operations.filter(function(op) {
75146                     return !isTouchMenu || !op.mouseOnly;
75147                 });
75148
75149                 if (!ops.length) return;
75150
75151                 _tooltips = [];
75152
75153                 // Position the menu above the anchor for stylus and finger input
75154                 // since the mapper's hand likely obscures the screen below the anchor
75155                 _menuTop = isTouchMenu;
75156
75157                 // Show labels for touch input since there aren't hover tooltips
75158                 var showLabels = isTouchMenu;
75159
75160                 var buttonHeight = showLabels ? 32 : 34;
75161                 if (showLabels) {
75162                     // Get a general idea of the width based on the length of the label
75163                     _menuWidth = 52 + Math.min(120, 6 * Math.max.apply(Math, ops.map(function(op) {
75164                         return op.title.length;
75165                     })));
75166                 } else {
75167                     _menuWidth = 44;
75168                 }
75169
75170                 _menuHeight = _verticalPadding * 2 + ops.length * buttonHeight;
75171
75172                 _menu = selection
75173                     .append('div')
75174                     .attr('class', 'edit-menu')
75175                     .classed('touch-menu', isTouchMenu)
75176                     .style('padding', _verticalPadding + 'px 0');
75177
75178                 var buttons = _menu.selectAll('.edit-menu-item')
75179                     .data(ops);
75180
75181                 // enter
75182                 var buttonsEnter = buttons.enter()
75183                     .append('button')
75184                     .attr('class', function (d) { return 'edit-menu-item edit-menu-item-' + d.id; })
75185                     .style('height', buttonHeight + 'px')
75186                     .on('click', click)
75187                     // don't listen for `mouseup` because we only care about non-mouse pointer types
75188                     .on('pointerup', pointerup)
75189                     .on('pointerdown mousedown', function pointerdown() {
75190                         // don't let button presses also act as map input - #1869
75191                         event.stopPropagation();
75192                     });
75193
75194                 buttonsEnter.each(function(d) {
75195                     var tooltip = uiTooltip()
75196                         .heading(d.title)
75197                         .title(d.tooltip())
75198                         .keys([d.keys[0]]);
75199
75200                     _tooltips.push(tooltip);
75201
75202                     select(this)
75203                         .call(tooltip)
75204                         .append('div')
75205                         .attr('class', 'icon-wrap')
75206                         .call(svgIcon('#iD-operation-' + d.id, 'operation'));
75207                 });
75208
75209                 if (showLabels) {
75210                     buttonsEnter.append('span')
75211                         .attr('class', 'label')
75212                         .text(function(d) {
75213                             return d.title;
75214                         });
75215                 }
75216
75217                 // update
75218                 buttons = buttonsEnter
75219                     .merge(buttons)
75220                     .classed('disabled', function(d) { return d.disabled(); });
75221
75222                 updatePosition();
75223
75224                 var initialScale = context.projection.scale();
75225                 context.map()
75226                     .on('move.edit-menu', function() {
75227                         if (initialScale !== context.projection.scale()) {
75228                             editMenu.close();
75229                         }
75230                     })
75231                     .on('drawn.edit-menu', function(info) {
75232                         if (info.full) updatePosition();
75233                     });
75234
75235                 var lastPointerUpType;
75236                 // `pointerup` is always called before `click`
75237                 function pointerup() {
75238                     lastPointerUpType = event.pointerType;
75239                 }
75240
75241                 function click(operation) {
75242                     event.stopPropagation();
75243                     if (operation.disabled()) {
75244                         if (lastPointerUpType === 'touch' ||
75245                             lastPointerUpType === 'pen') {
75246                             // there are no tooltips for touch interactions so flash feedback instead
75247                             context.ui().flash
75248                                 .duration(4000)
75249                                 .iconName('#iD-operation-' + operation.id)
75250                                 .iconClass('operation disabled')
75251                                 .text(operation.tooltip)();
75252                         }
75253                     } else {
75254                         if (lastPointerUpType === 'touch' ||
75255                             lastPointerUpType === 'pen') {
75256                             context.ui().flash
75257                                 .duration(2000)
75258                                 .iconName('#iD-operation-' + operation.id)
75259                                 .iconClass('operation')
75260                                 .text(operation.annotation() || operation.title)();
75261                         }
75262
75263                         operation();
75264                         editMenu.close();
75265                     }
75266                     lastPointerUpType = null;
75267                 }
75268
75269                 dispatch$1.call('toggled', this, true);
75270             };
75271
75272             function updatePosition() {
75273
75274                 if (!_menu || _menu.empty()) return;
75275
75276                 var anchorLoc = context.projection(_anchorLocLonLat);
75277
75278                 var viewport = context.surfaceRect();
75279
75280                 if (anchorLoc[0] < 0 ||
75281                     anchorLoc[0] > viewport.width ||
75282                     anchorLoc[1] < 0 ||
75283                     anchorLoc[1] > viewport.height) {
75284                     // close the menu if it's gone offscreen
75285
75286                     editMenu.close();
75287                     return;
75288                 }
75289
75290                 var menuLeft = displayOnLeft(viewport);
75291
75292                 var offset = [0, 0];
75293
75294                 offset[0] = menuLeft ? -1 * (_menuSideMargin + _menuWidth) : _menuSideMargin;
75295
75296                 if (_menuTop) {
75297                     if (anchorLoc[1] - _menuHeight < _vpTopMargin) {
75298                         // menu is near top viewport edge, shift downward
75299                         offset[1] = -anchorLoc[1] + _vpTopMargin;
75300                     } else {
75301                         offset[1] = -_menuHeight;
75302                     }
75303                 } else {
75304                     if (anchorLoc[1] + _menuHeight > (viewport.height - _vpBottomMargin)) {
75305                         // menu is near bottom viewport edge, shift upwards
75306                         offset[1] = -anchorLoc[1] - _menuHeight + viewport.height - _vpBottomMargin;
75307                     } else {
75308                         offset[1] = 0;
75309                     }
75310                 }
75311
75312                 var origin = geoVecAdd(anchorLoc, offset);
75313
75314                 _menu
75315                     .style('left', origin[0] + 'px')
75316                     .style('top', origin[1] + 'px');
75317
75318                 var tooltipSide = tooltipPosition(viewport, menuLeft);
75319                 _tooltips.forEach(function(tooltip) {
75320                     tooltip.placement(tooltipSide);
75321                 });
75322
75323                 function displayOnLeft(viewport) {
75324                     if (_mainLocalizer.textDirection() === 'ltr') {
75325                         if ((anchorLoc[0] + _menuSideMargin + _menuWidth) > (viewport.width - _vpSideMargin)) {
75326                             // right menu would be too close to the right viewport edge, go left
75327                             return true;
75328                         }
75329                         // prefer right menu
75330                         return false;
75331
75332                     } else { // rtl
75333                         if ((anchorLoc[0] - _menuSideMargin - _menuWidth) < _vpSideMargin) {
75334                             // left menu would be too close to the left viewport edge, go right
75335                             return false;
75336                         }
75337                         // prefer left menu
75338                         return true;
75339                     }
75340                 }
75341
75342                 function tooltipPosition(viewport, menuLeft) {
75343                     if (_mainLocalizer.textDirection() === 'ltr') {
75344                         if (menuLeft) {
75345                             // if there's not room for a right-side menu then there definitely
75346                             // isn't room for right-side tooltips
75347                             return 'left';
75348                         }
75349                         if ((anchorLoc[0] + _menuSideMargin + _menuWidth + _tooltipWidth) > (viewport.width - _vpSideMargin)) {
75350                             // right tooltips would be too close to the right viewport edge, go left
75351                             return 'left';
75352                         }
75353                         // prefer right tooltips
75354                         return 'right';
75355
75356                     } else { // rtl
75357                         if (!menuLeft) {
75358                             return 'right';
75359                         }
75360                         if ((anchorLoc[0] - _menuSideMargin - _menuWidth - _tooltipWidth) < _vpSideMargin) {
75361                             // left tooltips would be too close to the left viewport edge, go right
75362                             return 'right';
75363                         }
75364                         // prefer left tooltips
75365                         return 'left';
75366                     }
75367                 }
75368             }
75369
75370             editMenu.close = function () {
75371
75372                 context.map()
75373                     .on('move.edit-menu', null)
75374                     .on('drawn.edit-menu', null);
75375
75376                 _menu.remove();
75377                 _tooltips = [];
75378
75379                 dispatch$1.call('toggled', this, false);
75380             };
75381
75382             editMenu.anchorLoc = function(val) {
75383                 if (!arguments.length) return _anchorLoc;
75384                 _anchorLoc = val;
75385                 _anchorLocLonLat = context.projection.invert(_anchorLoc);
75386                 return editMenu;
75387             };
75388
75389             editMenu.triggerType = function(val) {
75390                 if (!arguments.length) return _triggerType;
75391                 _triggerType = val;
75392                 return editMenu;
75393             };
75394
75395             editMenu.operations = function(val) {
75396                 if (!arguments.length) return _operations;
75397                 _operations = val;
75398                 return editMenu;
75399             };
75400
75401             return utilRebind(editMenu, dispatch$1, 'on');
75402         }
75403
75404         function uiFeatureInfo(context) {
75405             function update(selection) {
75406                 var features = context.features();
75407                 var stats = features.stats();
75408                 var count = 0;
75409                 var hiddenList = features.hidden().map(function(k) {
75410                     if (stats[k]) {
75411                         count += stats[k];
75412                         return String(stats[k]) + ' ' + _t('feature.' + k + '.description');
75413                     }
75414                 }).filter(Boolean);
75415
75416                 selection.html('');
75417
75418                 if (hiddenList.length) {
75419                     var tooltipBehavior = uiTooltip()
75420                         .placement('top')
75421                         .title(function() {
75422                             return hiddenList.join('<br/>');
75423                         });
75424
75425                     selection.append('a')
75426                         .attr('class', 'chip')
75427                         .attr('href', '#')
75428                         .attr('tabindex', -1)
75429                         .html(_t('feature_info.hidden_warning', { count: count }))
75430                         .call(tooltipBehavior)
75431                         .on('click', function() {
75432                             tooltipBehavior.hide();
75433                             event.preventDefault();
75434                             // open the Map Data pane
75435                             context.ui().togglePanes(context.container().select('.map-panes .map-data-pane'));
75436                         });
75437                 }
75438
75439                 selection
75440                     .classed('hide', !hiddenList.length);
75441             }
75442
75443
75444             return function(selection) {
75445                 update(selection);
75446
75447                 context.features().on('change.feature_info', function() {
75448                     update(selection);
75449                 });
75450             };
75451         }
75452
75453         function uiFlash(context) {
75454             var _flashTimer;
75455
75456             var _duration = 2000;
75457             var _iconName = '#iD-icon-no';
75458             var _iconClass = 'disabled';
75459             var _text = '';
75460             var _textClass;
75461
75462             function flash() {
75463                 if (_flashTimer) {
75464                     _flashTimer.stop();
75465                 }
75466
75467                 context.container().select('.main-footer-wrap')
75468                     .classed('footer-hide', true)
75469                     .classed('footer-show', false);
75470                 context.container().select('.flash-wrap')
75471                     .classed('footer-hide', false)
75472                     .classed('footer-show', true);
75473
75474                 var content = context.container().select('.flash-wrap').selectAll('.flash-content')
75475                     .data([0]);
75476
75477                 // Enter
75478                 var contentEnter = content.enter()
75479                     .append('div')
75480                     .attr('class', 'flash-content');
75481
75482                 var iconEnter = contentEnter
75483                     .append('svg')
75484                     .attr('class', 'flash-icon icon')
75485                     .append('g')
75486                     .attr('transform', 'translate(10,10)');
75487
75488                 iconEnter
75489                     .append('circle')
75490                     .attr('r', 9);
75491
75492                 iconEnter
75493                     .append('use')
75494                     .attr('transform', 'translate(-7,-7)')
75495                     .attr('width', '14')
75496                     .attr('height', '14');
75497
75498                 contentEnter
75499                     .append('div')
75500                     .attr('class', 'flash-text');
75501
75502
75503                 // Update
75504                 content = content
75505                     .merge(contentEnter);
75506
75507                 content
75508                     .selectAll('.flash-icon')
75509                     .attr('class', 'icon flash-icon ' + (_iconClass || ''));
75510
75511                 content
75512                     .selectAll('.flash-icon use')
75513                     .attr('xlink:href', _iconName);
75514
75515                 content
75516                     .selectAll('.flash-text')
75517                     .attr('class', 'flash-text ' + (_textClass || ''))
75518                     .text(_text);
75519
75520
75521                 _flashTimer = d3_timeout(function() {
75522                     _flashTimer = null;
75523                     context.container().select('.main-footer-wrap')
75524                         .classed('footer-hide', false)
75525                         .classed('footer-show', true);
75526                     context.container().select('.flash-wrap')
75527                         .classed('footer-hide', true)
75528                         .classed('footer-show', false);
75529                 }, _duration);
75530
75531                 return content;
75532             }
75533
75534
75535             flash.duration = function(_) {
75536                 if (!arguments.length) return _duration;
75537                 _duration = _;
75538                 return flash;
75539             };
75540
75541             flash.text = function(_) {
75542                 if (!arguments.length) return _text;
75543                 _text = _;
75544                 return flash;
75545             };
75546
75547             flash.textClass = function(_) {
75548                 if (!arguments.length) return _textClass;
75549                 _textClass = _;
75550                 return flash;
75551             };
75552
75553             flash.iconName = function(_) {
75554                 if (!arguments.length) return _iconName;
75555                 _iconName = _;
75556                 return flash;
75557             };
75558
75559             flash.iconClass = function(_) {
75560                 if (!arguments.length) return _iconClass;
75561                 _iconClass = _;
75562                 return flash;
75563             };
75564
75565             return flash;
75566         }
75567
75568         function uiFullScreen(context) {
75569             var element = context.container().node();
75570             // var button = d3_select(null);
75571
75572
75573             function getFullScreenFn() {
75574                 if (element.requestFullscreen) {
75575                     return element.requestFullscreen;
75576                 } else if (element.msRequestFullscreen) {
75577                     return element.msRequestFullscreen;
75578                 } else if (element.mozRequestFullScreen) {
75579                     return element.mozRequestFullScreen;
75580                 } else if (element.webkitRequestFullscreen) {
75581                     return element.webkitRequestFullscreen;
75582                 }
75583             }
75584
75585
75586             function getExitFullScreenFn() {
75587                 if (document.exitFullscreen) {
75588                     return document.exitFullscreen;
75589                 } else if (document.msExitFullscreen) {
75590                     return document.msExitFullscreen;
75591                 } else if (document.mozCancelFullScreen) {
75592                     return document.mozCancelFullScreen;
75593                 } else if (document.webkitExitFullscreen) {
75594                     return document.webkitExitFullscreen;
75595                 }
75596             }
75597
75598
75599             function isFullScreen() {
75600                 return document.fullscreenElement ||
75601                     document.mozFullScreenElement ||
75602                     document.webkitFullscreenElement ||
75603                     document.msFullscreenElement;
75604             }
75605
75606
75607             function isSupported() {
75608                 return !!getFullScreenFn();
75609             }
75610
75611
75612             function fullScreen() {
75613                 event.preventDefault();
75614                 if (!isFullScreen()) {
75615                     // button.classed('active', true);
75616                     getFullScreenFn().apply(element);
75617                 } else {
75618                     // button.classed('active', false);
75619                     getExitFullScreenFn().apply(document);
75620                 }
75621             }
75622
75623
75624             return function() { // selection) {
75625                 if (!isSupported()) return;
75626
75627                 // button = selection.append('button')
75628                 //     .attr('title', t('full_screen'))
75629                 //     .attr('tabindex', -1)
75630                 //     .on('click', fullScreen)
75631                 //     .call(tooltip);
75632
75633                 // button.append('span')
75634                 //     .attr('class', 'icon full-screen');
75635
75636                 var detected = utilDetect();
75637                 var keys = (detected.os === 'mac' ? [uiCmd('⌃⌘F'), 'f11'] : ['f11']);
75638                 context.keybinding().on(keys, fullScreen);
75639             };
75640         }
75641
75642         function uiGeolocate(context) {
75643             var _geolocationOptions = {
75644                 // prioritize speed and power usage over precision
75645                 enableHighAccuracy: false,
75646                 // don't hang indefinitely getting the location
75647                 timeout: 6000 // 6sec
75648             };
75649             var _locating = uiLoading(context).message(_t('geolocate.locating')).blocking(true);
75650             var _layer = context.layers().layer('geolocate');
75651             var _position;
75652             var _extent;
75653             var _timeoutID;
75654             var _button = select(null);
75655
75656             function click() {
75657                 if (context.inIntro()) return;
75658                 if (!_layer.enabled() && !_locating.isShown()) {
75659
75660                     // This timeout ensures that we still call finish() even if
75661                     // the user declines to share their location in Firefox
75662                     _timeoutID = setTimeout(error, 10000 /* 10sec */ );
75663
75664                     context.container().call(_locating);
75665                     // get the latest position even if we already have one
75666                     navigator.geolocation.getCurrentPosition(success, error, _geolocationOptions);
75667                 } else {
75668                     _locating.close();
75669                     _layer.enabled(null, false);
75670                     updateButtonState();
75671                 }
75672             }
75673
75674             function zoomTo() {
75675                 context.enter(modeBrowse(context));
75676
75677                 var map = context.map();
75678                 _layer.enabled(_position, true);
75679                 updateButtonState();
75680                 map.centerZoomEase(_extent.center(), Math.min(20, map.extentZoom(_extent)));
75681             }
75682
75683             function success(geolocation) {
75684                 _position = geolocation;
75685                 var coords = _position.coords;
75686                 _extent = geoExtent([coords.longitude, coords.latitude]).padByMeters(coords.accuracy);
75687                 zoomTo();
75688                 finish();
75689             }
75690
75691             function error() {
75692                 if (_position) {
75693                     // use the position from a previous call if we have one
75694                     zoomTo();
75695                 } else {
75696                     context.ui().flash
75697                         .text(_t('geolocate.location_unavailable'))
75698                         .iconName('#iD-icon-geolocate')();
75699                 }
75700
75701                 finish();
75702             }
75703
75704             function finish() {
75705                 _locating.close();  // unblock ui
75706                 if (_timeoutID) { clearTimeout(_timeoutID); }
75707                 _timeoutID = undefined;
75708             }
75709
75710             function updateButtonState() {
75711                 _button.classed('active', _layer.enabled());
75712             }
75713
75714             return function(selection) {
75715                 if (!navigator.geolocation || !navigator.geolocation.getCurrentPosition) return;
75716
75717                 _button = selection
75718                     .append('button')
75719                     .on('click', click)
75720                     .call(svgIcon('#iD-icon-geolocate', 'light'))
75721                     .call(uiTooltip()
75722                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
75723                         .title(_t('geolocate.title'))
75724                         .keys([_t('geolocate.key')])
75725                     );
75726
75727                 context.keybinding().on(_t('geolocate.key'), click);
75728             };
75729         }
75730
75731         function uiPanelBackground(context) {
75732             var background = context.background();
75733             var currSourceName = null;
75734             var metadata = {};
75735             var metadataKeys = [
75736                 'zoom', 'vintage', 'source', 'description', 'resolution', 'accuracy'
75737             ];
75738
75739             var debouncedRedraw = debounce(redraw, 250);
75740
75741             function redraw(selection) {
75742                 var source = background.baseLayerSource();
75743                 if (!source) return;
75744
75745                 var isDG = (source.id.match(/^DigitalGlobe/i) !== null);
75746
75747                 if (currSourceName !== source.name()) {
75748                     currSourceName = source.name();
75749                     metadata = {};
75750                 }
75751
75752                 selection.html('');
75753
75754                 var list = selection
75755                     .append('ul')
75756                     .attr('class', 'background-info');
75757
75758                 list
75759                     .append('li')
75760                     .text(currSourceName);
75761
75762                 metadataKeys.forEach(function(k) {
75763                     // DigitalGlobe vintage is available in raster layers for now.
75764                     if (isDG && k === 'vintage') return;
75765
75766                     list
75767                         .append('li')
75768                         .attr('class', 'background-info-list-' + k)
75769                         .classed('hide', !metadata[k])
75770                         .text(_t('info_panels.background.' + k) + ':')
75771                         .append('span')
75772                         .attr('class', 'background-info-span-' + k)
75773                         .text(metadata[k]);
75774                 });
75775
75776                 debouncedGetMetadata(selection);
75777
75778                 var toggleTiles = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles';
75779
75780                 selection
75781                     .append('a')
75782                     .text(_t('info_panels.background.' + toggleTiles))
75783                     .attr('href', '#')
75784                     .attr('class', 'button button-toggle-tiles')
75785                     .on('click', function() {
75786                         event.preventDefault();
75787                         context.setDebug('tile', !context.getDebug('tile'));
75788                         selection.call(redraw);
75789                     });
75790
75791                 if (isDG) {
75792                     var key = source.id + '-vintage';
75793                     var sourceVintage = context.background().findSource(key);
75794                     var showsVintage = context.background().showsLayer(sourceVintage);
75795                     var toggleVintage = showsVintage ? 'hide_vintage' : 'show_vintage';
75796                     selection
75797                         .append('a')
75798                         .text(_t('info_panels.background.' + toggleVintage))
75799                         .attr('href', '#')
75800                         .attr('class', 'button button-toggle-vintage')
75801                         .on('click', function() {
75802                             event.preventDefault();
75803                             context.background().toggleOverlayLayer(sourceVintage);
75804                             selection.call(redraw);
75805                         });
75806                 }
75807
75808                 // disable if necessary
75809                 ['DigitalGlobe-Premium', 'DigitalGlobe-Standard'].forEach(function(layerId) {
75810                     if (source.id !== layerId) {
75811                         var key = layerId + '-vintage';
75812                         var sourceVintage = context.background().findSource(key);
75813                         if (context.background().showsLayer(sourceVintage)) {
75814                             context.background().toggleOverlayLayer(sourceVintage);
75815                         }
75816                     }
75817                 });
75818             }
75819
75820
75821             var debouncedGetMetadata = debounce(getMetadata, 250);
75822
75823             function getMetadata(selection) {
75824                 var tile = context.container().select('.layer-background img.tile-center');   // tile near viewport center
75825                 if (tile.empty()) return;
75826
75827                 var sourceName = currSourceName;
75828                 var d = tile.datum();
75829                 var zoom = (d && d.length >= 3 && d[2]) || Math.floor(context.map().zoom());
75830                 var center = context.map().center();
75831
75832                 // update zoom
75833                 metadata.zoom = String(zoom);
75834                 selection.selectAll('.background-info-list-zoom')
75835                     .classed('hide', false)
75836                     .selectAll('.background-info-span-zoom')
75837                     .text(metadata.zoom);
75838
75839                 if (!d || !d.length >= 3) return;
75840
75841                 background.baseLayerSource().getMetadata(center, d, function(err, result) {
75842                     if (err || currSourceName !== sourceName) return;
75843
75844                     // update vintage
75845                     var vintage = result.vintage;
75846                     metadata.vintage = (vintage && vintage.range) || _t('info_panels.background.unknown');
75847                     selection.selectAll('.background-info-list-vintage')
75848                         .classed('hide', false)
75849                         .selectAll('.background-info-span-vintage')
75850                         .text(metadata.vintage);
75851
75852                     // update other metdata
75853                     metadataKeys.forEach(function(k) {
75854                         if (k === 'zoom' || k === 'vintage') return;  // done already
75855                         var val = result[k];
75856                         metadata[k] = val;
75857                         selection.selectAll('.background-info-list-' + k)
75858                             .classed('hide', !val)
75859                             .selectAll('.background-info-span-' + k)
75860                             .text(val);
75861                     });
75862                 });
75863             }
75864
75865
75866             var panel = function(selection) {
75867                 selection.call(redraw);
75868
75869                 context.map()
75870                     .on('drawn.info-background', function() {
75871                         selection.call(debouncedRedraw);
75872                     })
75873                     .on('move.info-background', function() {
75874                         selection.call(debouncedGetMetadata);
75875                     });
75876
75877             };
75878
75879             panel.off = function() {
75880                 context.map()
75881                     .on('drawn.info-background', null)
75882                     .on('move.info-background', null);
75883             };
75884
75885             panel.id = 'background';
75886             panel.title = _t('info_panels.background.title');
75887             panel.key = _t('info_panels.background.key');
75888
75889
75890             return panel;
75891         }
75892
75893         function uiPanelHistory(context) {
75894             var osm;
75895
75896             function displayTimestamp(timestamp) {
75897                 if (!timestamp) return _t('info_panels.history.unknown');
75898                 var options = {
75899                     day: 'numeric', month: 'short', year: 'numeric',
75900                     hour: 'numeric', minute: 'numeric', second: 'numeric'
75901                 };
75902                 var d = new Date(timestamp);
75903                 if (isNaN(d.getTime())) return _t('info_panels.history.unknown');
75904                 return d.toLocaleString(_mainLocalizer.localeCode(), options);
75905             }
75906
75907
75908             function displayUser(selection, userName) {
75909                 if (!userName) {
75910                     selection
75911                         .append('span')
75912                         .text(_t('info_panels.history.unknown'));
75913                     return;
75914                 }
75915
75916                 selection
75917                     .append('span')
75918                     .attr('class', 'user-name')
75919                     .text(userName);
75920
75921                 var links = selection
75922                     .append('div')
75923                     .attr('class', 'links');
75924
75925                 if (osm) {
75926                     links
75927                         .append('a')
75928                         .attr('class', 'user-osm-link')
75929                         .attr('href', osm.userURL(userName))
75930                         .attr('target', '_blank')
75931                         .attr('tabindex', -1)
75932                         .text('OSM');
75933                 }
75934
75935                 links
75936                     .append('a')
75937                     .attr('class', 'user-hdyc-link')
75938                     .attr('href', 'https://hdyc.neis-one.org/?' + userName)
75939                     .attr('target', '_blank')
75940                     .attr('tabindex', -1)
75941                     .text('HDYC');
75942             }
75943
75944
75945             function displayChangeset(selection, changeset) {
75946                 if (!changeset) {
75947                     selection
75948                         .append('span')
75949                         .text(_t('info_panels.history.unknown'));
75950                     return;
75951                 }
75952
75953                 selection
75954                     .append('span')
75955                     .attr('class', 'changeset-id')
75956                     .text(changeset);
75957
75958                 var links = selection
75959                     .append('div')
75960                     .attr('class', 'links');
75961
75962                 if (osm) {
75963                     links
75964                         .append('a')
75965                         .attr('class', 'changeset-osm-link')
75966                         .attr('href', osm.changesetURL(changeset))
75967                         .attr('target', '_blank')
75968                         .attr('tabindex', -1)
75969                         .text('OSM');
75970                 }
75971
75972                 links
75973                     .append('a')
75974                     .attr('class', 'changeset-osmcha-link')
75975                     .attr('href', 'https://osmcha.org/changesets/' + changeset)
75976                     .attr('target', '_blank')
75977                     .attr('tabindex', -1)
75978                     .text('OSMCha');
75979
75980                 links
75981                     .append('a')
75982                     .attr('class', 'changeset-achavi-link')
75983                     .attr('href', 'https://overpass-api.de/achavi/?changeset=' + changeset)
75984                     .attr('target', '_blank')
75985                     .attr('tabindex', -1)
75986                     .text('Achavi');
75987             }
75988
75989
75990             function redraw(selection) {
75991                 var selectedNoteID = context.selectedNoteID();
75992                 osm = context.connection();
75993
75994                 var selected, note, entity;
75995                 if (selectedNoteID && osm) {       // selected 1 note
75996                     selected = [ _t('note.note') + ' ' + selectedNoteID ];
75997                     note = osm.getNote(selectedNoteID);
75998                 } else {                           // selected 1..n entities
75999                     selected = context.selectedIDs()
76000                         .filter(function(e) { return context.hasEntity(e); });
76001                     if (selected.length) {
76002                         entity = context.entity(selected[0]);
76003                     }
76004                 }
76005
76006                 var singular = selected.length === 1 ? selected[0] : null;
76007
76008                 selection.html('');
76009
76010                 selection
76011                     .append('h4')
76012                     .attr('class', 'history-heading')
76013                     .text(singular || _t('info_panels.history.selected', { n: selected.length }));
76014
76015                 if (!singular) return;
76016
76017                 if (entity) {
76018                     selection.call(redrawEntity, entity);
76019                 } else if (note) {
76020                     selection.call(redrawNote, note);
76021                 }
76022             }
76023
76024
76025             function redrawNote(selection, note) {
76026                 if (!note || note.isNew()) {
76027                     selection
76028                         .append('div')
76029                         .text(_t('info_panels.history.note_no_history'));
76030                     return;
76031                 }
76032
76033                 var list = selection
76034                     .append('ul');
76035
76036                 list
76037                     .append('li')
76038                     .text(_t('info_panels.history.note_comments') + ':')
76039                     .append('span')
76040                     .text(note.comments.length);
76041
76042                 if (note.comments.length) {
76043                     list
76044                         .append('li')
76045                         .text(_t('info_panels.history.note_created_date') + ':')
76046                         .append('span')
76047                         .text(displayTimestamp(note.comments[0].date));
76048
76049                     list
76050                         .append('li')
76051                         .text(_t('info_panels.history.note_created_user') + ':')
76052                         .call(displayUser, note.comments[0].user);
76053                 }
76054
76055                 if (osm) {
76056                     selection
76057                         .append('a')
76058                         .attr('class', 'view-history-on-osm')
76059                         .attr('target', '_blank')
76060                         .attr('tabindex', -1)
76061                         .attr('href', osm.noteURL(note))
76062                         .call(svgIcon('#iD-icon-out-link', 'inline'))
76063                         .append('span')
76064                         .text(_t('info_panels.history.note_link_text'));
76065                 }
76066             }
76067
76068
76069             function redrawEntity(selection, entity) {
76070                 if (!entity || entity.isNew()) {
76071                     selection
76072                         .append('div')
76073                         .text(_t('info_panels.history.no_history'));
76074                     return;
76075                 }
76076
76077                 var links = selection
76078                     .append('div')
76079                     .attr('class', 'links');
76080
76081                 if (osm) {
76082                     links
76083                         .append('a')
76084                         .attr('class', 'view-history-on-osm')
76085                         .attr('href', osm.historyURL(entity))
76086                         .attr('target', '_blank')
76087                         .attr('tabindex', -1)
76088                         .attr('title', _t('info_panels.history.link_text'))
76089                         .text('OSM');
76090                 }
76091                 links
76092                     .append('a')
76093                     .attr('class', 'pewu-history-viewer-link')
76094                     .attr('href', 'https://pewu.github.io/osm-history/#/' + entity.type + '/' + entity.osmId())
76095                     .attr('target', '_blank')
76096                     .attr('tabindex', -1)
76097                     .text('PeWu');
76098
76099                 var list = selection
76100                     .append('ul');
76101
76102                 list
76103                     .append('li')
76104                     .text(_t('info_panels.history.version') + ':')
76105                     .append('span')
76106                     .text(entity.version);
76107
76108                 list
76109                     .append('li')
76110                     .text(_t('info_panels.history.last_edit') + ':')
76111                     .append('span')
76112                     .text(displayTimestamp(entity.timestamp));
76113
76114                 list
76115                     .append('li')
76116                     .text(_t('info_panels.history.edited_by') + ':')
76117                     .call(displayUser, entity.user);
76118
76119                 list
76120                     .append('li')
76121                     .text(_t('info_panels.history.changeset') + ':')
76122                     .call(displayChangeset, entity.changeset);
76123             }
76124
76125
76126             var panel = function(selection) {
76127                 selection.call(redraw);
76128
76129                 context.map()
76130                     .on('drawn.info-history', function() {
76131                         selection.call(redraw);
76132                     });
76133
76134                 context
76135                     .on('enter.info-history', function() {
76136                         selection.call(redraw);
76137                     });
76138             };
76139
76140             panel.off = function() {
76141                 context.map().on('drawn.info-history', null);
76142                 context.on('enter.info-history', null);
76143             };
76144
76145             panel.id = 'history';
76146             panel.title = _t('info_panels.history.title');
76147             panel.key = _t('info_panels.history.key');
76148
76149
76150             return panel;
76151         }
76152
76153         var OSM_PRECISION = 7;
76154
76155         /**
76156          * Returns a localized representation of the given length measurement.
76157          *
76158          * @param {Number} m area in meters
76159          * @param {Boolean} isImperial true for U.S. customary units; false for metric
76160          */
76161         function displayLength(m, isImperial) {
76162             var d = m * (isImperial ? 3.28084 : 1);
76163             var unit;
76164
76165             if (isImperial) {
76166                 if (d >= 5280) {
76167                     d /= 5280;
76168                     unit = 'miles';
76169                 } else {
76170                     unit = 'feet';
76171                 }
76172             } else {
76173                 if (d >= 1000) {
76174                     d /= 1000;
76175                     unit = 'kilometers';
76176                 } else {
76177                     unit = 'meters';
76178                 }
76179             }
76180
76181             return _t('units.' + unit, {
76182                 quantity: d.toLocaleString(_mainLocalizer.localeCode(), {
76183                     maximumSignificantDigits: 4
76184                 })
76185             });
76186         }
76187
76188         /**
76189          * Returns a localized representation of the given area measurement.
76190          *
76191          * @param {Number} m2 area in square meters
76192          * @param {Boolean} isImperial true for U.S. customary units; false for metric
76193          */
76194         function displayArea(m2, isImperial) {
76195             var locale = _mainLocalizer.localeCode();
76196             var d = m2 * (isImperial ? 10.7639111056 : 1);
76197             var d1, d2, area;
76198             var unit1 = '';
76199             var unit2 = '';
76200
76201             if (isImperial) {
76202                 if (d >= 6969600) { // > 0.25mi² show mi²
76203                     d1 = d / 27878400;
76204                     unit1 = 'square_miles';
76205                 } else {
76206                     d1 = d;
76207                     unit1 = 'square_feet';
76208                 }
76209
76210                 if (d > 4356 && d < 43560000) { // 0.1 - 1000 acres
76211                     d2 = d / 43560;
76212                     unit2 = 'acres';
76213                 }
76214
76215             } else {
76216                 if (d >= 250000) { // > 0.25km² show km²
76217                     d1 = d / 1000000;
76218                     unit1 = 'square_kilometers';
76219                 } else {
76220                     d1 = d;
76221                     unit1 = 'square_meters';
76222                 }
76223
76224                 if (d > 1000 && d < 10000000) { // 0.1 - 1000 hectares
76225                     d2 = d / 10000;
76226                     unit2 = 'hectares';
76227                 }
76228             }
76229
76230             area = _t('units.' + unit1, {
76231                 quantity: d1.toLocaleString(locale, {
76232                     maximumSignificantDigits: 4
76233                 })
76234             });
76235
76236             if (d2) {
76237                 return _t('units.area_pair', {
76238                     area1: area,
76239                     area2: _t('units.' + unit2, {
76240                         quantity: d2.toLocaleString(locale, {
76241                             maximumSignificantDigits: 2
76242                         })
76243                     })
76244                 });
76245             } else {
76246                 return area;
76247             }
76248         }
76249
76250         function wrap(x, min, max) {
76251             var d = max - min;
76252             return ((x - min) % d + d) % d + min;
76253         }
76254
76255         function clamp$1(x, min, max) {
76256             return Math.max(min, Math.min(x, max));
76257         }
76258
76259         function displayCoordinate(deg, pos, neg) {
76260             var locale = _mainLocalizer.localeCode();
76261             var min = (Math.abs(deg) - Math.floor(Math.abs(deg))) * 60;
76262             var sec = (min - Math.floor(min)) * 60;
76263             var displayDegrees = _t('units.arcdegrees', {
76264                 quantity: Math.floor(Math.abs(deg)).toLocaleString(locale)
76265             });
76266             var displayCoordinate;
76267
76268             if (Math.floor(sec) > 0) {
76269                 displayCoordinate = displayDegrees +
76270                     _t('units.arcminutes', {
76271                         quantity: Math.floor(min).toLocaleString(locale)
76272                     }) +
76273                     _t('units.arcseconds', {
76274                         quantity: Math.round(sec).toLocaleString(locale)
76275                     });
76276             } else if (Math.floor(min) > 0) {
76277                 displayCoordinate = displayDegrees +
76278                     _t('units.arcminutes', {
76279                         quantity: Math.round(min).toLocaleString(locale)
76280                     });
76281             } else {
76282                 displayCoordinate = _t('units.arcdegrees', {
76283                     quantity: Math.round(Math.abs(deg)).toLocaleString(locale)
76284                 });
76285             }
76286
76287             if (deg === 0) {
76288                 return displayCoordinate;
76289             } else {
76290                 return _t('units.coordinate', {
76291                     coordinate: displayCoordinate,
76292                     direction: _t('units.' + (deg > 0 ? pos : neg))
76293                 });
76294             }
76295         }
76296
76297         /**
76298          * Returns given coordinate pair in degree-minute-second format.
76299          *
76300          * @param {Array<Number>} coord longitude and latitude
76301          */
76302         function dmsCoordinatePair(coord) {
76303             return _t('units.coordinate_pair', {
76304                 latitude: displayCoordinate(clamp$1(coord[1], -90, 90), 'north', 'south'),
76305                 longitude: displayCoordinate(wrap(coord[0], -180, 180), 'east', 'west')
76306             });
76307         }
76308
76309         /**
76310          * Returns the given coordinate pair in decimal format.
76311          * note: unlocalized to avoid comma ambiguity - see #4765
76312          *
76313          * @param {Array<Number>} coord longitude and latitude
76314          */
76315         function decimalCoordinatePair(coord) {
76316             return _t('units.coordinate_pair', {
76317                 latitude: clamp$1(coord[1], -90, 90).toFixed(OSM_PRECISION),
76318                 longitude: wrap(coord[0], -180, 180).toFixed(OSM_PRECISION)
76319             });
76320         }
76321
76322         function uiPanelLocation(context) {
76323             var currLocation = '';
76324
76325
76326             function redraw(selection) {
76327                 selection.html('');
76328
76329                 var list = selection
76330                     .append('ul');
76331
76332                 // Mouse coordinates
76333                 var coord = context.map().mouseCoordinates();
76334                 if (coord.some(isNaN)) {
76335                     coord = context.map().center();
76336                 }
76337
76338                 list
76339                     .append('li')
76340                     .text(dmsCoordinatePair(coord))
76341                     .append('li')
76342                     .text(decimalCoordinatePair(coord));
76343
76344                 // Location Info
76345                 selection
76346                     .append('div')
76347                     .attr('class', 'location-info')
76348                     .text(currLocation || ' ');
76349
76350                 debouncedGetLocation(selection, coord);
76351             }
76352
76353
76354             var debouncedGetLocation = debounce(getLocation, 250);
76355             function getLocation(selection, coord) {
76356                 if (!services.geocoder) {
76357                     currLocation = _t('info_panels.location.unknown_location');
76358                     selection.selectAll('.location-info')
76359                         .text(currLocation);
76360                 } else {
76361                     services.geocoder.reverse(coord, function(err, result) {
76362                         currLocation = result ? result.display_name : _t('info_panels.location.unknown_location');
76363                         selection.selectAll('.location-info')
76364                             .text(currLocation);
76365                     });
76366                 }
76367             }
76368
76369
76370             var panel = function(selection) {
76371                 selection.call(redraw);
76372
76373                 context.surface()
76374                     .on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'move.info-location', function() {
76375                         selection.call(redraw);
76376                     });
76377             };
76378
76379             panel.off = function() {
76380                 context.surface()
76381                     .on('.info-location', null);
76382             };
76383
76384             panel.id = 'location';
76385             panel.title = _t('info_panels.location.title');
76386             panel.key = _t('info_panels.location.key');
76387
76388
76389             return panel;
76390         }
76391
76392         function uiPanelMeasurement(context) {
76393             var locale = _mainLocalizer.localeCode();
76394             var isImperial = !_mainLocalizer.usesMetric();
76395
76396
76397             function radiansToMeters(r) {
76398                 // using WGS84 authalic radius (6371007.1809 m)
76399                 return r * 6371007.1809;
76400             }
76401
76402             function steradiansToSqmeters(r) {
76403                 // http://gis.stackexchange.com/a/124857/40446
76404                 return r / (4 * Math.PI) * 510065621724000;
76405             }
76406
76407
76408             function toLineString(feature) {
76409                 if (feature.type === 'LineString') return feature;
76410
76411                 var result = { type: 'LineString', coordinates: [] };
76412                 if (feature.type === 'Polygon') {
76413                     result.coordinates = feature.coordinates[0];
76414                 } else if (feature.type === 'MultiPolygon') {
76415                     result.coordinates = feature.coordinates[0][0];
76416                 }
76417
76418                 return result;
76419             }
76420
76421
76422             function redraw(selection) {
76423                 var graph = context.graph();
76424                 var selectedNoteID = context.selectedNoteID();
76425                 var osm = services.osm;
76426
76427                 var heading;
76428                 var center, location, centroid;
76429                 var closed, geometry;
76430                 var totalNodeCount, length = 0, area = 0;
76431
76432                 if (selectedNoteID && osm) {       // selected 1 note
76433
76434                     var note = osm.getNote(selectedNoteID);
76435                     heading = _t('note.note') + ' ' + selectedNoteID;
76436                     location = note.loc;
76437                     geometry = 'note';
76438
76439                 } else {                           // selected 1..n entities
76440                     var selectedIDs = context.selectedIDs().filter(function(id) {
76441                         return context.hasEntity(id);
76442                     });
76443                     var selected = selectedIDs.map(function(id) {
76444                         return context.entity(id);
76445                     });
76446
76447                     heading = selected.length === 1 ? selected[0].id :
76448                         _t('info_panels.measurement.selected', { n: selected.length.toLocaleString(locale) });
76449
76450                     if (selected.length) {
76451                         var extent = geoExtent();
76452                         for (var i in selected) {
76453                             var entity = selected[i];
76454                             extent._extend(entity.extent(graph));
76455
76456                             geometry = entity.geometry(graph);
76457                             if (geometry === 'line' || geometry === 'area') {
76458                                 closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate());
76459                                 var feature = entity.asGeoJSON(graph);
76460                                 length += radiansToMeters(d3_geoLength(toLineString(feature)));
76461                                 centroid = d3_geoCentroid(feature);
76462                                 if (closed) {
76463                                     area += steradiansToSqmeters(entity.area(graph));
76464                                 }
76465                             }
76466                         }
76467
76468                         if (selected.length > 1) {
76469                             geometry = null;
76470                             closed = null;
76471                             centroid = null;
76472                         }
76473
76474                         if (selected.length === 1 && selected[0].type === 'node') {
76475                             location = selected[0].loc;
76476                         } else {
76477                             totalNodeCount = utilGetAllNodes(selectedIDs, context.graph()).length;
76478                         }
76479
76480                         if (!location && !centroid) {
76481                             center = extent.center();
76482                         }
76483                     }
76484                 }
76485
76486                 selection.html('');
76487
76488                 if (heading) {
76489                     selection
76490                         .append('h4')
76491                         .attr('class', 'measurement-heading')
76492                         .text(heading);
76493                 }
76494
76495                 var list = selection
76496                     .append('ul');
76497                 var coordItem;
76498
76499                 if (geometry) {
76500                     list
76501                         .append('li')
76502                         .text(_t('info_panels.measurement.geometry') + ':')
76503                         .append('span')
76504                         .text(
76505                             closed ? _t('info_panels.measurement.closed_' + geometry) : _t('geometry.' + geometry)
76506                         );
76507                 }
76508
76509                 if (totalNodeCount) {
76510                     list
76511                         .append('li')
76512                         .text(_t('info_panels.measurement.node_count') + ':')
76513                         .append('span')
76514                         .text(totalNodeCount.toLocaleString(locale));
76515                 }
76516
76517                 if (area) {
76518                     list
76519                         .append('li')
76520                         .text(_t('info_panels.measurement.area') + ':')
76521                         .append('span')
76522                         .text(displayArea(area, isImperial));
76523                 }
76524
76525                 if (length) {
76526                     var lengthLabel = _t('info_panels.measurement.' + (closed ? 'perimeter' : 'length'));
76527                     list
76528                         .append('li')
76529                         .text(lengthLabel + ':')
76530                         .append('span')
76531                         .text(displayLength(length, isImperial));
76532                 }
76533
76534                 if (location) {
76535                     coordItem = list
76536                         .append('li')
76537                         .text(_t('info_panels.measurement.location') + ':');
76538                     coordItem.append('span')
76539                         .text(dmsCoordinatePair(location));
76540                     coordItem.append('span')
76541                         .text(decimalCoordinatePair(location));
76542                 }
76543
76544                 if (centroid) {
76545                     coordItem = list
76546                         .append('li')
76547                         .text(_t('info_panels.measurement.centroid') + ':');
76548                     coordItem.append('span')
76549                         .text(dmsCoordinatePair(centroid));
76550                     coordItem.append('span')
76551                         .text(decimalCoordinatePair(centroid));
76552                 }
76553
76554                 if (center) {
76555                     coordItem = list
76556                         .append('li')
76557                         .text(_t('info_panels.measurement.center') + ':');
76558                     coordItem.append('span')
76559                         .text(dmsCoordinatePair(center));
76560                     coordItem.append('span')
76561                         .text(decimalCoordinatePair(center));
76562                 }
76563
76564                 if (length || area) {
76565                     var toggle  = isImperial ? 'imperial' : 'metric';
76566                     selection
76567                         .append('a')
76568                         .text(_t('info_panels.measurement.' + toggle))
76569                         .attr('href', '#')
76570                         .attr('class', 'button button-toggle-units')
76571                         .on('click', function() {
76572                             event.preventDefault();
76573                             isImperial = !isImperial;
76574                             selection.call(redraw);
76575                         });
76576                 }
76577             }
76578
76579
76580             var panel = function(selection) {
76581                 selection.call(redraw);
76582
76583                 context.map()
76584                     .on('drawn.info-measurement', function() {
76585                         selection.call(redraw);
76586                     });
76587
76588                 context
76589                     .on('enter.info-measurement', function() {
76590                         selection.call(redraw);
76591                     });
76592             };
76593
76594             panel.off = function() {
76595                 context.map().on('drawn.info-measurement', null);
76596                 context.on('enter.info-measurement', null);
76597             };
76598
76599             panel.id = 'measurement';
76600             panel.title = _t('info_panels.measurement.title');
76601             panel.key = _t('info_panels.measurement.key');
76602
76603
76604             return panel;
76605         }
76606
76607         var uiInfoPanels = {
76608             background: uiPanelBackground,
76609             history: uiPanelHistory,
76610             location: uiPanelLocation,
76611             measurement: uiPanelMeasurement,
76612         };
76613
76614         function uiInfo(context) {
76615             var ids = Object.keys(uiInfoPanels);
76616             var wasActive = ['measurement'];
76617             var panels = {};
76618             var active = {};
76619
76620             // create panels
76621             ids.forEach(function(k) {
76622                 if (!panels[k]) {
76623                     panels[k] = uiInfoPanels[k](context);
76624                     active[k] = false;
76625                 }
76626             });
76627
76628
76629             function info(selection) {
76630
76631                 function redraw() {
76632                     var activeids = ids.filter(function(k) { return active[k]; }).sort();
76633
76634                     var containers = infoPanels.selectAll('.panel-container')
76635                         .data(activeids, function(k) { return k; });
76636
76637                     containers.exit()
76638                         .style('opacity', 1)
76639                         .transition()
76640                         .duration(200)
76641                         .style('opacity', 0)
76642                         .on('end', function(d) {
76643                             select(this)
76644                                 .call(panels[d].off)
76645                                 .remove();
76646                         });
76647
76648                     var enter = containers.enter()
76649                         .append('div')
76650                         .attr('class', function(d) { return 'fillD2 panel-container panel-container-' + d; });
76651
76652                     enter
76653                         .style('opacity', 0)
76654                         .transition()
76655                         .duration(200)
76656                         .style('opacity', 1);
76657
76658                     var title = enter
76659                         .append('div')
76660                         .attr('class', 'panel-title fillD2');
76661
76662                     title
76663                         .append('h3')
76664                         .text(function(d) { return panels[d].title; });
76665
76666                     title
76667                         .append('button')
76668                         .attr('class', 'close')
76669                         .on('click', function (d) { info.toggle(d); })
76670                         .call(svgIcon('#iD-icon-close'));
76671
76672                     enter
76673                         .append('div')
76674                         .attr('class', function(d) { return 'panel-content panel-content-' + d; });
76675
76676
76677                     // redraw the panels
76678                     infoPanels.selectAll('.panel-content')
76679                         .each(function(d) {
76680                             select(this).call(panels[d]);
76681                         });
76682                 }
76683
76684
76685                 info.toggle = function(which) {
76686                     if (event) {
76687                         event.stopImmediatePropagation();
76688                         event.preventDefault();
76689                     }
76690
76691                     var activeids = ids.filter(function(k) { return active[k]; });
76692
76693                     if (which) {  // toggle one
76694                         active[which] = !active[which];
76695                         if (activeids.length === 1 && activeids[0] === which) {  // none active anymore
76696                             wasActive = [which];
76697                         }
76698
76699                         context.container().select('.' + which + '-panel-toggle-item')
76700                             .classed('active', active[which])
76701                             .select('input')
76702                             .property('checked', active[which]);
76703
76704                     } else {      // toggle all
76705                         if (activeids.length) {
76706                             wasActive = activeids;
76707                             activeids.forEach(function(k) { active[k] = false; });
76708                         } else {
76709                             wasActive.forEach(function(k) { active[k] = true; });
76710                         }
76711                     }
76712
76713                     redraw();
76714                 };
76715
76716
76717                 var infoPanels = selection.selectAll('.info-panels')
76718                     .data([0]);
76719
76720                 infoPanels = infoPanels.enter()
76721                     .append('div')
76722                     .attr('class', 'info-panels')
76723                     .merge(infoPanels);
76724
76725                 redraw();
76726
76727                 context.keybinding()
76728                     .on(uiCmd('⌘' + _t('info_panels.key')), info.toggle);
76729
76730                 ids.forEach(function(k) {
76731                     var key = _t('info_panels.' + k + '.key', { default: null });
76732                     if (!key) return;
76733                     context.keybinding()
76734                         .on(uiCmd('⌘⇧' + key), function() { info.toggle(k); });
76735                 });
76736             }
76737
76738             return info;
76739         }
76740
76741         function pointBox(loc, context) {
76742             var rect = context.surfaceRect();
76743             var point = context.curtainProjection(loc);
76744             return {
76745                 left: point[0] + rect.left - 40,
76746                 top: point[1] + rect.top - 60,
76747                 width: 80,
76748                 height: 90
76749             };
76750         }
76751
76752
76753         function pad(locOrBox, padding, context) {
76754             var box;
76755             if (locOrBox instanceof Array) {
76756                 var rect = context.surfaceRect();
76757                 var point = context.curtainProjection(locOrBox);
76758                 box = {
76759                     left: point[0] + rect.left,
76760                     top: point[1] + rect.top
76761                 };
76762             } else {
76763                 box = locOrBox;
76764             }
76765
76766             return {
76767                 left: box.left - padding,
76768                 top: box.top - padding,
76769                 width: (box.width || 0) + 2 * padding,
76770                 height: (box.width || 0) + 2 * padding
76771             };
76772         }
76773
76774
76775         function icon(name, svgklass, useklass) {
76776             return '<svg class="icon ' + (svgklass || '') + '">' +
76777                  '<use xlink:href="' + name + '"' +
76778                  (useklass ? ' class="' + useklass + '"' : '') + '></use></svg>';
76779         }
76780
76781         var helpStringReplacements;
76782
76783         // Returns the localized string for `id` with a standardized set of icon, key, and
76784         // label replacements suitable for tutorials and documentation. Optionally supplemented
76785         // with custom `replacements`
76786         function helpString(id, replacements) {
76787             // only load these the first time
76788             if (!helpStringReplacements) helpStringReplacements = {
76789                 // insert icons corresponding to various UI elements
76790                 point_icon: icon('#iD-icon-point', 'pre-text'),
76791                 line_icon: icon('#iD-icon-line', 'pre-text'),
76792                 area_icon: icon('#iD-icon-area', 'pre-text'),
76793                 note_icon: icon('#iD-icon-note', 'pre-text add-note'),
76794                 plus: icon('#iD-icon-plus', 'pre-text'),
76795                 minus: icon('#iD-icon-minus', 'pre-text'),
76796                 move_icon: icon('#iD-operation-move', 'pre-text operation'),
76797                 merge_icon: icon('#iD-operation-merge', 'pre-text operation'),
76798                 delete_icon: icon('#iD-operation-delete', 'pre-text operation'),
76799                 circularize_icon: icon('#iD-operation-circularize', 'pre-text operation'),
76800                 split_icon: icon('#iD-operation-split', 'pre-text operation'),
76801                 orthogonalize_icon: icon('#iD-operation-orthogonalize', 'pre-text operation'),
76802                 disconnect_icon: icon('#iD-operation-disconnect', 'pre-text operation'),
76803                 layers_icon: icon('#iD-icon-layers', 'pre-text'),
76804                 data_icon: icon('#iD-icon-data', 'pre-text'),
76805                 inspect: icon('#iD-icon-inspect', 'pre-text'),
76806                 help_icon: icon('#iD-icon-help', 'pre-text'),
76807                 undo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo', 'pre-text'),
76808                 redo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-undo' : '#iD-icon-redo', 'pre-text'),
76809                 save_icon: icon('#iD-icon-save', 'pre-text'),
76810                 leftclick: icon('#iD-walkthrough-mouse-left', 'pre-text operation'),
76811                 rightclick: icon('#iD-walkthrough-mouse-right', 'pre-text operation'),
76812                 mousewheel_icon: icon('#iD-walkthrough-mousewheel', 'pre-text operation'),
76813                 tap_icon: icon('#iD-walkthrough-tap', 'pre-text operation'),
76814                 doubletap_icon: icon('#iD-walkthrough-doubletap', 'pre-text operation'),
76815                 longpress_icon: icon('#iD-walkthrough-longpress', 'pre-text operation'),
76816                 touchdrag_icon: icon('#iD-walkthrough-touchdrag', 'pre-text operation'),
76817                 pinch_icon: icon('#iD-walkthrough-pinch-apart', 'pre-text operation'),
76818
76819                 // insert keys; may be localized and platform-dependent
76820                 shift: uiCmd.display('⇧'),
76821                 alt: uiCmd.display('⌥'),
76822                 return: uiCmd.display('↵'),
76823                 esc: _t('shortcuts.key.esc'),
76824                 space: _t('shortcuts.key.space'),
76825                 add_note_key: _t('modes.add_note.key'),
76826                 help_key: _t('help.key'),
76827                 shortcuts_key: _t('shortcuts.toggle.key'),
76828
76829                 // reference localized UI labels directly so that they'll always match
76830                 save: _t('save.title'),
76831                 undo: _t('undo.title'),
76832                 redo: _t('redo.title'),
76833                 upload: _t('commit.save'),
76834                 point: _t('modes.add_point.title'),
76835                 line: _t('modes.add_line.title'),
76836                 area: _t('modes.add_area.title'),
76837                 note: _t('modes.add_note.title'),
76838                 delete: _t('operations.delete.title'),
76839                 move: _t('operations.move.title'),
76840                 orthogonalize: _t('operations.orthogonalize.title'),
76841                 circularize: _t('operations.circularize.title'),
76842                 merge: _t('operations.merge.title'),
76843                 disconnect: _t('operations.disconnect.title'),
76844                 split: _t('operations.split.title'),
76845                 map_data: _t('map_data.title'),
76846                 osm_notes: _t('map_data.layers.notes.title'),
76847                 fields: _t('inspector.fields'),
76848                 tags: _t('inspector.tags'),
76849                 relations: _t('inspector.relations'),
76850                 new_relation: _t('inspector.new_relation'),
76851                 turn_restrictions: _t('presets.fields.restrictions.label'),
76852                 background_settings: _t('background.description'),
76853                 imagery_offset: _t('background.fix_misalignment'),
76854                 start_the_walkthrough: _t('splash.walkthrough'),
76855                 help: _t('help.title'),
76856                 ok: _t('intro.ok')
76857             };
76858
76859             var reps;
76860             if (replacements) {
76861                 reps = Object.assign(replacements, helpStringReplacements);
76862             } else {
76863                 reps = helpStringReplacements;
76864             }
76865
76866             return _t(id, reps)
76867                  // use keyboard key styling for shortcuts
76868                 .replace(/\`(.*?)\`/g, '<kbd>$1</kbd>');
76869         }
76870
76871
76872         function slugify(text) {
76873             return text.toString().toLowerCase()
76874                 .replace(/\s+/g, '-')           // Replace spaces with -
76875                 .replace(/[^\w\-]+/g, '')       // Remove all non-word chars
76876                 .replace(/\-\-+/g, '-')         // Replace multiple - with single -
76877                 .replace(/^-+/, '')             // Trim - from start of text
76878                 .replace(/-+$/, '');            // Trim - from end of text
76879         }
76880
76881
76882         // console warning for missing walkthrough names
76883         var missingStrings = {};
76884         function checkKey(key, text) {
76885             if (_t(key, { default: undefined}) === undefined) {
76886                 if (missingStrings.hasOwnProperty(key)) return;  // warn once
76887                 missingStrings[key] = text;
76888                 var missing = key + ': ' + text;
76889                 if (typeof console !== 'undefined') console.log(missing); // eslint-disable-line
76890             }
76891         }
76892
76893
76894         function localize(obj) {
76895             var key;
76896
76897             // Assign name if entity has one..
76898             var name = obj.tags && obj.tags.name;
76899             if (name) {
76900                 key = 'intro.graph.name.' + slugify(name);
76901                 obj.tags.name = _t(key, { default: name });
76902                 checkKey(key, name);
76903             }
76904
76905             // Assign street name if entity has one..
76906             var street = obj.tags && obj.tags['addr:street'];
76907             if (street) {
76908                 key = 'intro.graph.name.' + slugify(street);
76909                 obj.tags['addr:street'] = _t(key, { default: street });
76910                 checkKey(key, street);
76911
76912                 // Add address details common across walkthrough..
76913                 var addrTags = [
76914                     'block_number', 'city', 'county', 'district', 'hamlet', 'neighbourhood',
76915                     'postcode', 'province', 'quarter', 'state', 'subdistrict', 'suburb'
76916                 ];
76917                 addrTags.forEach(function(k) {
76918                     var key = 'intro.graph.' + k;
76919                     var tag = 'addr:' + k;
76920                     var val = obj.tags && obj.tags[tag];
76921                     var str = _t(key, { default: val });
76922
76923                     if (str) {
76924                         if (str.match(/^<.*>$/) !== null) {
76925                             delete obj.tags[tag];
76926                         } else {
76927                             obj.tags[tag] = str;
76928                         }
76929                     }
76930                 });
76931             }
76932
76933             return obj;
76934         }
76935
76936
76937         // Used to detect squareness.. some duplicataion of code from actionOrthogonalize.
76938         function isMostlySquare(points) {
76939             // note: uses 15 here instead of the 12 from actionOrthogonalize because
76940             // actionOrthogonalize can actually straighten some larger angles as it iterates
76941             var threshold = 15; // degrees within right or straight
76942             var lowerBound = Math.cos((90 - threshold) * Math.PI / 180);  // near right
76943             var upperBound = Math.cos(threshold * Math.PI / 180);         // near straight
76944
76945             for (var i = 0; i < points.length; i++) {
76946                 var a = points[(i - 1 + points.length) % points.length];
76947                 var origin = points[i];
76948                 var b = points[(i + 1) % points.length];
76949
76950                 var dotp = geoVecNormalizedDot(a, b, origin);
76951                 var mag = Math.abs(dotp);
76952                 if (mag > lowerBound && mag < upperBound) {
76953                     return false;
76954                 }
76955             }
76956
76957             return true;
76958         }
76959
76960
76961         function selectMenuItem(context, operation) {
76962             return context.container().select('.edit-menu .edit-menu-item-' + operation);
76963         }
76964
76965
76966         function transitionTime(point1, point2) {
76967             var distance = geoSphericalDistance(point1, point2);
76968             if (distance === 0)
76969                 return 0;
76970             else if (distance < 80)
76971                 return 500;
76972             else
76973                 return 1000;
76974         }
76975
76976         // Tooltips and svg mask used to highlight certain features
76977         function uiCurtain(containerNode) {
76978
76979             var surface = select(null),
76980                 tooltip = select(null),
76981                 darkness = select(null);
76982
76983             function curtain(selection) {
76984                 surface = selection
76985                     .append('svg')
76986                     .attr('class', 'curtain')
76987                     .style('top', 0)
76988                     .style('left', 0);
76989
76990                 darkness = surface.append('path')
76991                     .attr('x', 0)
76992                     .attr('y', 0)
76993                     .attr('class', 'curtain-darkness');
76994
76995                 select(window).on('resize.curtain', resize);
76996
76997                 tooltip = selection.append('div')
76998                     .attr('class', 'tooltip');
76999
77000                 tooltip
77001                     .append('div')
77002                     .attr('class', 'popover-arrow');
77003
77004                 tooltip
77005                     .append('div')
77006                     .attr('class', 'popover-inner');
77007
77008                 resize();
77009
77010
77011                 function resize() {
77012                     surface
77013                         .attr('width', containerNode.clientWidth)
77014                         .attr('height', containerNode.clientHeight);
77015                     curtain.cut(darkness.datum());
77016                 }
77017             }
77018
77019
77020             /**
77021              * Reveal cuts the curtain to highlight the given box,
77022              * and shows a tooltip with instructions next to the box.
77023              *
77024              * @param  {String|ClientRect} [box]   box used to cut the curtain
77025              * @param  {String}    [text]          text for a tooltip
77026              * @param  {Object}    [options]
77027              * @param  {string}    [options.tooltipClass]    optional class to add to the tooltip
77028              * @param  {integer}   [options.duration]        transition time in milliseconds
77029              * @param  {string}    [options.buttonText]      if set, create a button with this text label
77030              * @param  {function}  [options.buttonCallback]  if set, the callback for the button
77031              * @param  {function}  [options.padding]         extra margin in px to put around bbox
77032              * @param  {String|ClientRect} [options.tooltipBox]  box for tooltip position, if different from box for the curtain
77033              */
77034             curtain.reveal = function(box, text, options) {
77035                 options = options || {};
77036
77037                 if (typeof box === 'string') {
77038                     box = select(box).node();
77039                 }
77040                 if (box && box.getBoundingClientRect) {
77041                     box = copyBox(box.getBoundingClientRect());
77042                     var containerRect = containerNode.getBoundingClientRect();
77043                     box.top -= containerRect.top;
77044                     box.left -= containerRect.left;
77045                 }
77046                 if (box && options.padding) {
77047                     box.top -= options.padding;
77048                     box.left -= options.padding;
77049                     box.bottom += options.padding;
77050                     box.right += options.padding;
77051                     box.height += options.padding * 2;
77052                     box.width += options.padding * 2;
77053                 }
77054
77055                 var tooltipBox;
77056                 if (options.tooltipBox) {
77057                     tooltipBox = options.tooltipBox;
77058                     if (typeof tooltipBox === 'string') {
77059                         tooltipBox = select(tooltipBox).node();
77060                     }
77061                     if (tooltipBox && tooltipBox.getBoundingClientRect) {
77062                         tooltipBox = copyBox(tooltipBox.getBoundingClientRect());
77063                     }
77064                 } else {
77065                     tooltipBox = box;
77066                 }
77067
77068                 if (tooltipBox && text) {
77069                     // pseudo markdown bold text for the instruction section..
77070                     var parts = text.split('**');
77071                     var html = parts[0] ? '<span>' + parts[0] + '</span>' : '';
77072                     if (parts[1]) {
77073                         html += '<span class="instruction">' + parts[1] + '</span>';
77074                     }
77075
77076                     html = html.replace(/\*(.*?)\*/g, '<em>$1</em>');   // emphasis
77077                     html = html.replace(/\{br\}/g, '<br/><br/>');       // linebreak
77078
77079                     if (options.buttonText && options.buttonCallback) {
77080                         html += '<div class="button-section">' +
77081                             '<button href="#" class="button action">' + options.buttonText + '</button></div>';
77082                     }
77083
77084                     var classes = 'curtain-tooltip popover tooltip arrowed in ' + (options.tooltipClass || '');
77085                     tooltip
77086                         .classed(classes, true)
77087                         .selectAll('.popover-inner')
77088                         .html(html);
77089
77090                     if (options.buttonText && options.buttonCallback) {
77091                         var button = tooltip.selectAll('.button-section .button.action');
77092                         button
77093                             .on('click', function() {
77094                                 event.preventDefault();
77095                                 options.buttonCallback();
77096                             });
77097                     }
77098
77099                     var tip = copyBox(tooltip.node().getBoundingClientRect()),
77100                         w = containerNode.clientWidth,
77101                         h = containerNode.clientHeight,
77102                         tooltipWidth = 200,
77103                         tooltipArrow = 5,
77104                         side, pos;
77105
77106
77107                     // hack: this will have bottom placement,
77108                     // so need to reserve extra space for the tooltip illustration.
77109                     if (options.tooltipClass === 'intro-mouse') {
77110                         tip.height += 80;
77111                     }
77112
77113                     // trim box dimensions to just the portion that fits in the container..
77114                     if (tooltipBox.top + tooltipBox.height > h) {
77115                         tooltipBox.height -= (tooltipBox.top + tooltipBox.height - h);
77116                     }
77117                     if (tooltipBox.left + tooltipBox.width > w) {
77118                         tooltipBox.width -= (tooltipBox.left + tooltipBox.width - w);
77119                     }
77120
77121                     // determine tooltip placement..
77122
77123                     if (tooltipBox.top + tooltipBox.height < 100) {
77124                         // tooltip below box..
77125                         side = 'bottom';
77126                         pos = [
77127                             tooltipBox.left + tooltipBox.width / 2 - tip.width / 2,
77128                             tooltipBox.top + tooltipBox.height
77129                         ];
77130
77131                     } else if (tooltipBox.top > h - 140) {
77132                         // tooltip above box..
77133                         side = 'top';
77134                         pos = [
77135                             tooltipBox.left + tooltipBox.width / 2 - tip.width / 2,
77136                             tooltipBox.top - tip.height
77137                         ];
77138
77139                     } else {
77140                         // tooltip to the side of the tooltipBox..
77141                         var tipY = tooltipBox.top + tooltipBox.height / 2 - tip.height / 2;
77142
77143                         if (_mainLocalizer.textDirection() === 'rtl') {
77144                             if (tooltipBox.left - tooltipWidth - tooltipArrow < 70) {
77145                                 side = 'right';
77146                                 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
77147
77148                             } else {
77149                                 side = 'left';
77150                                 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
77151                             }
77152
77153                         } else {
77154                             if (tooltipBox.left + tooltipBox.width + tooltipArrow + tooltipWidth > w - 70) {
77155                                 side = 'left';
77156                                 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
77157                             }
77158                             else {
77159                                 side = 'right';
77160                                 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
77161                             }
77162                         }
77163                     }
77164
77165                     if (options.duration !== 0 || !tooltip.classed(side)) {
77166                         tooltip.call(uiToggle(true));
77167                     }
77168
77169                     tooltip
77170                         .style('top', pos[1] + 'px')
77171                         .style('left', pos[0] + 'px')
77172                         .attr('class', classes + ' ' + side);
77173
77174
77175                     // shift popover-inner if it is very close to the top or bottom edge
77176                     // (doesn't affect the placement of the popover-arrow)
77177                     var shiftY = 0;
77178                     if (side === 'left' || side === 'right') {
77179                         if (pos[1] < 60) {
77180                             shiftY = 60 - pos[1];
77181                         }
77182                         else if (pos[1] + tip.height > h - 100) {
77183                             shiftY = h - pos[1] - tip.height - 100;
77184                         }
77185                     }
77186                     tooltip.selectAll('.popover-inner')
77187                         .style('top', shiftY + 'px');
77188
77189                 } else {
77190                     tooltip
77191                         .classed('in', false)
77192                         .call(uiToggle(false));
77193                 }
77194
77195                 curtain.cut(box, options.duration);
77196
77197                 return tooltip;
77198             };
77199
77200
77201             curtain.cut = function(datum, duration) {
77202                 darkness.datum(datum)
77203                     .interrupt();
77204
77205                 var selection;
77206                 if (duration === 0) {
77207                     selection = darkness;
77208                 } else {
77209                     selection = darkness
77210                         .transition()
77211                         .duration(duration || 600)
77212                         .ease(linear$1);
77213                 }
77214
77215                 selection
77216                     .attr('d', function(d) {
77217                         var containerWidth = containerNode.clientWidth;
77218                         var containerHeight = containerNode.clientHeight;
77219                         var string = 'M 0,0 L 0,' + containerHeight + ' L ' +
77220                             containerWidth + ',' + containerHeight + 'L' +
77221                             containerWidth + ',0 Z';
77222
77223                         if (!d) return string;
77224                         return string + 'M' +
77225                             d.left + ',' + d.top + 'L' +
77226                             d.left + ',' + (d.top + d.height) + 'L' +
77227                             (d.left + d.width) + ',' + (d.top + d.height) + 'L' +
77228                             (d.left + d.width) + ',' + (d.top) + 'Z';
77229
77230                     });
77231             };
77232
77233
77234             curtain.remove = function() {
77235                 surface.remove();
77236                 tooltip.remove();
77237                 select(window).on('resize.curtain', null);
77238             };
77239
77240
77241             // ClientRects are immutable, so copy them to an object,
77242             // in case we need to trim the height/width.
77243             function copyBox(src) {
77244                 return {
77245                     top: src.top,
77246                     right: src.right,
77247                     bottom: src.bottom,
77248                     left: src.left,
77249                     width: src.width,
77250                     height: src.height
77251                 };
77252             }
77253
77254
77255             return curtain;
77256         }
77257
77258         function uiIntroWelcome(context, reveal) {
77259             var dispatch$1 = dispatch('done');
77260
77261             var chapter = {
77262                 title: 'intro.welcome.title'
77263             };
77264
77265
77266             function welcome() {
77267                 context.map().centerZoom([-85.63591, 41.94285], 19);
77268                 reveal('.intro-nav-wrap .chapter-welcome',
77269                     helpString('intro.welcome.welcome'),
77270                     { buttonText: _t('intro.ok'), buttonCallback: practice }
77271                 );
77272             }
77273
77274             function practice() {
77275                 reveal('.intro-nav-wrap .chapter-welcome',
77276                     helpString('intro.welcome.practice'),
77277                     { buttonText: _t('intro.ok'), buttonCallback: words }
77278                 );
77279             }
77280
77281             function words() {
77282                 reveal('.intro-nav-wrap .chapter-welcome',
77283                     helpString('intro.welcome.words'),
77284                     { buttonText: _t('intro.ok'), buttonCallback: chapters }
77285                 );
77286             }
77287
77288
77289             function chapters() {
77290                 dispatch$1.call('done');
77291                 reveal('.intro-nav-wrap .chapter-navigation',
77292                     helpString('intro.welcome.chapters', { next: _t('intro.navigation.title') })
77293                 );
77294             }
77295
77296
77297             chapter.enter = function() {
77298                 welcome();
77299             };
77300
77301
77302             chapter.exit = function() {
77303                 context.container().select('.curtain-tooltip.intro-mouse')
77304                     .selectAll('.counter')
77305                     .remove();
77306             };
77307
77308
77309             chapter.restart = function() {
77310                 chapter.exit();
77311                 chapter.enter();
77312             };
77313
77314
77315             return utilRebind(chapter, dispatch$1, 'on');
77316         }
77317
77318         function uiIntroNavigation(context, reveal) {
77319             var dispatch$1 = dispatch('done');
77320             var timeouts = [];
77321             var hallId = 'n2061';
77322             var townHall = [-85.63591, 41.94285];
77323             var springStreetId = 'w397';
77324             var springStreetEndId = 'n1834';
77325             var springStreet = [-85.63582, 41.94255];
77326             var onewayField = _mainPresetIndex.field('oneway');
77327             var maxspeedField = _mainPresetIndex.field('maxspeed');
77328
77329
77330             var chapter = {
77331                 title: 'intro.navigation.title'
77332             };
77333
77334
77335             function timeout(f, t) {
77336                 timeouts.push(window.setTimeout(f, t));
77337             }
77338
77339
77340             function eventCancel() {
77341                 event.stopPropagation();
77342                 event.preventDefault();
77343             }
77344
77345
77346             function isTownHallSelected() {
77347                 var ids = context.selectedIDs();
77348                 return ids.length === 1 && ids[0] === hallId;
77349             }
77350
77351
77352             function dragMap() {
77353                 context.enter(modeBrowse(context));
77354                 context.history().reset('initial');
77355
77356                 var msec = transitionTime(townHall, context.map().center());
77357                 if (msec) { reveal(null, null, { duration: 0 }); }
77358                 context.map().centerZoomEase(townHall, 19, msec);
77359
77360                 timeout(function() {
77361                     var centerStart = context.map().center();
77362
77363                     var textId = context.lastPointerType() === 'mouse' ? 'drag' : 'drag_touch';
77364                     var dragString = helpString('intro.navigation.map_info') + '{br}' + helpString('intro.navigation.' + textId);
77365                     reveal('.surface', dragString);
77366                     context.map().on('drawn.intro', function() {
77367                         reveal('.surface', dragString, { duration: 0 });
77368                     });
77369
77370                     context.map().on('move.intro', function() {
77371                         var centerNow = context.map().center();
77372                         if (centerStart[0] !== centerNow[0] || centerStart[1] !== centerNow[1]) {
77373                             context.map().on('move.intro', null);
77374                             timeout(function() { continueTo(zoomMap); }, 3000);
77375                         }
77376                     });
77377
77378                 }, msec + 100);
77379
77380                 function continueTo(nextStep) {
77381                     context.map().on('move.intro drawn.intro', null);
77382                     nextStep();
77383                 }
77384             }
77385
77386
77387             function zoomMap() {
77388                 var zoomStart = context.map().zoom();
77389
77390                 var textId = context.lastPointerType() === 'mouse' ? 'zoom' : 'zoom_touch';
77391                 var zoomString = helpString('intro.navigation.' + textId);
77392
77393                 reveal('.surface', zoomString);
77394
77395                 context.map().on('drawn.intro', function() {
77396                     reveal('.surface', zoomString, { duration: 0 });
77397                 });
77398
77399                 context.map().on('move.intro', function() {
77400                     if (context.map().zoom() !== zoomStart) {
77401                         context.map().on('move.intro', null);
77402                         timeout(function() { continueTo(features); }, 3000);
77403                     }
77404                 });
77405
77406                 function continueTo(nextStep) {
77407                     context.map().on('move.intro drawn.intro', null);
77408                     nextStep();
77409                 }
77410             }
77411
77412
77413             function features() {
77414                 var onClick = function() { continueTo(pointsLinesAreas); };
77415
77416                 reveal('.surface', helpString('intro.navigation.features'),
77417                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77418                 );
77419
77420                 context.map().on('drawn.intro', function() {
77421                     reveal('.surface', helpString('intro.navigation.features'),
77422                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77423                     );
77424                 });
77425
77426                 function continueTo(nextStep) {
77427                     context.map().on('drawn.intro', null);
77428                     nextStep();
77429                 }
77430             }
77431
77432             function pointsLinesAreas() {
77433                 var onClick = function() { continueTo(nodesWays); };
77434
77435                 reveal('.surface', helpString('intro.navigation.points_lines_areas'),
77436                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77437                 );
77438
77439                 context.map().on('drawn.intro', function() {
77440                     reveal('.surface', helpString('intro.navigation.points_lines_areas'),
77441                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77442                     );
77443                 });
77444
77445                 function continueTo(nextStep) {
77446                     context.map().on('drawn.intro', null);
77447                     nextStep();
77448                 }
77449             }
77450
77451             function nodesWays() {
77452                 var onClick = function() { continueTo(clickTownHall); };
77453
77454                 reveal('.surface', helpString('intro.navigation.nodes_ways'),
77455                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77456                 );
77457
77458                 context.map().on('drawn.intro', function() {
77459                     reveal('.surface', helpString('intro.navigation.nodes_ways'),
77460                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77461                     );
77462                 });
77463
77464                 function continueTo(nextStep) {
77465                     context.map().on('drawn.intro', null);
77466                     nextStep();
77467                 }
77468             }
77469
77470             function clickTownHall() {
77471                 context.enter(modeBrowse(context));
77472                 context.history().reset('initial');
77473
77474                 var entity = context.hasEntity(hallId);
77475                 if (!entity) return;
77476                 reveal(null, null, { duration: 0 });
77477                 context.map().centerZoomEase(entity.loc, 19, 500);
77478
77479                 timeout(function() {
77480                     var entity = context.hasEntity(hallId);
77481                     if (!entity) return;
77482                     var box = pointBox(entity.loc, context);
77483                     var textId = context.lastPointerType() === 'mouse' ? 'click_townhall' : 'tap_townhall';
77484                     reveal(box, helpString('intro.navigation.' + textId));
77485
77486                     context.map().on('move.intro drawn.intro', function() {
77487                         var entity = context.hasEntity(hallId);
77488                         if (!entity) return;
77489                         var box = pointBox(entity.loc, context);
77490                         reveal(box, helpString('intro.navigation.' + textId), { duration: 0 });
77491                     });
77492
77493                     context.on('enter.intro', function() {
77494                         if (isTownHallSelected()) continueTo(selectedTownHall);
77495                     });
77496
77497                 }, 550);  // after centerZoomEase
77498
77499                 context.history().on('change.intro', function() {
77500                     if (!context.hasEntity(hallId)) {
77501                         continueTo(clickTownHall);
77502                     }
77503                 });
77504
77505                 function continueTo(nextStep) {
77506                     context.on('enter.intro', null);
77507                     context.map().on('move.intro drawn.intro', null);
77508                     context.history().on('change.intro', null);
77509                     nextStep();
77510                 }
77511             }
77512
77513
77514             function selectedTownHall() {
77515                 if (!isTownHallSelected()) return clickTownHall();
77516
77517                 var entity = context.hasEntity(hallId);
77518                 if (!entity) return clickTownHall();
77519
77520                 var box = pointBox(entity.loc, context);
77521                 var onClick = function() { continueTo(editorTownHall); };
77522
77523                 reveal(box, helpString('intro.navigation.selected_townhall'),
77524                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77525                 );
77526
77527                 context.map().on('move.intro drawn.intro', function() {
77528                     var entity = context.hasEntity(hallId);
77529                     if (!entity) return;
77530                     var box = pointBox(entity.loc, context);
77531                     reveal(box, helpString('intro.navigation.selected_townhall'),
77532                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77533                     );
77534                 });
77535
77536                 context.history().on('change.intro', function() {
77537                     if (!context.hasEntity(hallId)) {
77538                         continueTo(clickTownHall);
77539                     }
77540                 });
77541
77542                 function continueTo(nextStep) {
77543                     context.map().on('move.intro drawn.intro', null);
77544                     context.history().on('change.intro', null);
77545                     nextStep();
77546                 }
77547             }
77548
77549
77550             function editorTownHall() {
77551                 if (!isTownHallSelected()) return clickTownHall();
77552
77553                 // disallow scrolling
77554                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77555
77556                 var onClick = function() { continueTo(presetTownHall); };
77557
77558                 reveal('.entity-editor-pane',
77559                     helpString('intro.navigation.editor_townhall'),
77560                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77561                 );
77562
77563                 context.on('exit.intro', function() {
77564                     continueTo(clickTownHall);
77565                 });
77566
77567                 context.history().on('change.intro', function() {
77568                     if (!context.hasEntity(hallId)) {
77569                         continueTo(clickTownHall);
77570                     }
77571                 });
77572
77573                 function continueTo(nextStep) {
77574                     context.on('exit.intro', null);
77575                     context.history().on('change.intro', null);
77576                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77577                     nextStep();
77578                 }
77579             }
77580
77581
77582             function presetTownHall() {
77583                 if (!isTownHallSelected()) return clickTownHall();
77584
77585                 // reset pane, in case user happened to change it..
77586                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
77587                 // disallow scrolling
77588                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77589
77590                 // preset match, in case the user happened to change it.
77591                 var entity = context.entity(context.selectedIDs()[0]);
77592                 var preset = _mainPresetIndex.match(entity, context.graph());
77593
77594                 var onClick = function() { continueTo(fieldsTownHall); };
77595
77596                 reveal('.entity-editor-pane .section-feature-type',
77597                     helpString('intro.navigation.preset_townhall', { preset: preset.name() }),
77598                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77599                 );
77600
77601                 context.on('exit.intro', function() {
77602                     continueTo(clickTownHall);
77603                 });
77604
77605                 context.history().on('change.intro', function() {
77606                     if (!context.hasEntity(hallId)) {
77607                         continueTo(clickTownHall);
77608                     }
77609                 });
77610
77611                 function continueTo(nextStep) {
77612                     context.on('exit.intro', null);
77613                     context.history().on('change.intro', null);
77614                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77615                     nextStep();
77616                 }
77617             }
77618
77619
77620             function fieldsTownHall() {
77621                 if (!isTownHallSelected()) return clickTownHall();
77622
77623                 // reset pane, in case user happened to change it..
77624                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
77625                 // disallow scrolling
77626                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77627
77628                 var onClick = function() { continueTo(closeTownHall); };
77629
77630                 reveal('.entity-editor-pane .section-preset-fields',
77631                     helpString('intro.navigation.fields_townhall'),
77632                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77633                 );
77634
77635                 context.on('exit.intro', function() {
77636                     continueTo(clickTownHall);
77637                 });
77638
77639                 context.history().on('change.intro', function() {
77640                     if (!context.hasEntity(hallId)) {
77641                         continueTo(clickTownHall);
77642                     }
77643                 });
77644
77645                 function continueTo(nextStep) {
77646                     context.on('exit.intro', null);
77647                     context.history().on('change.intro', null);
77648                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77649                     nextStep();
77650                 }
77651             }
77652
77653
77654             function closeTownHall() {
77655                 if (!isTownHallSelected()) return clickTownHall();
77656
77657                 var selector = '.entity-editor-pane button.close svg use';
77658                 var href = select(selector).attr('href') || '#iD-icon-close';
77659
77660                 reveal('.entity-editor-pane',
77661                     helpString('intro.navigation.close_townhall', { button: icon(href, 'pre-text') })
77662                 );
77663
77664                 context.on('exit.intro', function() {
77665                     continueTo(searchStreet);
77666                 });
77667
77668                 context.history().on('change.intro', function() {
77669                     // update the close icon in the tooltip if the user edits something.
77670                     var selector = '.entity-editor-pane button.close svg use';
77671                     var href = select(selector).attr('href') || '#iD-icon-close';
77672
77673                     reveal('.entity-editor-pane',
77674                         helpString('intro.navigation.close_townhall', { button: icon(href, 'pre-text') }),
77675                         { duration: 0 }
77676                     );
77677                 });
77678
77679                 function continueTo(nextStep) {
77680                     context.on('exit.intro', null);
77681                     context.history().on('change.intro', null);
77682                     nextStep();
77683                 }
77684             }
77685
77686
77687             function searchStreet() {
77688                 context.enter(modeBrowse(context));
77689                 context.history().reset('initial');  // ensure spring street exists
77690
77691                 var msec = transitionTime(springStreet, context.map().center());
77692                 if (msec) { reveal(null, null, { duration: 0 }); }
77693                 context.map().centerZoomEase(springStreet, 19, msec);  // ..and user can see it
77694
77695                 timeout(function() {
77696                     reveal('.search-header input',
77697                         helpString('intro.navigation.search_street', { name: _t('intro.graph.name.spring-street') })
77698                     );
77699
77700                     context.container().select('.search-header input')
77701                         .on('keyup.intro', checkSearchResult);
77702                 }, msec + 100);
77703             }
77704
77705
77706             function checkSearchResult() {
77707                 var first = context.container().select('.feature-list-item:nth-child(0n+2)');  // skip "No Results" item
77708                 var firstName = first.select('.entity-name');
77709                 var name = _t('intro.graph.name.spring-street');
77710
77711                 if (!firstName.empty() && firstName.text() === name) {
77712                     reveal(first.node(),
77713                         helpString('intro.navigation.choose_street', { name: name }),
77714                         { duration: 300 }
77715                     );
77716
77717                     context.on('exit.intro', function() {
77718                         continueTo(selectedStreet);
77719                     });
77720
77721                     context.container().select('.search-header input')
77722                         .on('keydown.intro', eventCancel, true)
77723                         .on('keyup.intro', null);
77724                 }
77725
77726                 function continueTo(nextStep) {
77727                     context.on('exit.intro', null);
77728                     context.container().select('.search-header input')
77729                         .on('keydown.intro', null)
77730                         .on('keyup.intro', null);
77731                     nextStep();
77732                 }
77733             }
77734
77735
77736             function selectedStreet() {
77737                 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
77738                     return searchStreet();
77739                 }
77740
77741                 var onClick = function() { continueTo(editorStreet); };
77742                 var entity = context.entity(springStreetEndId);
77743                 var box = pointBox(entity.loc, context);
77744                 box.height = 500;
77745
77746                 reveal(box,
77747                     helpString('intro.navigation.selected_street', { name: _t('intro.graph.name.spring-street') }),
77748                     { duration: 600, buttonText: _t('intro.ok'), buttonCallback: onClick }
77749                 );
77750
77751                 timeout(function() {
77752                     context.map().on('move.intro drawn.intro', function() {
77753                         var entity = context.hasEntity(springStreetEndId);
77754                         if (!entity) return;
77755                         var box = pointBox(entity.loc, context);
77756                         box.height = 500;
77757                         reveal(box,
77758                             helpString('intro.navigation.selected_street', { name: _t('intro.graph.name.spring-street') }),
77759                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77760                         );
77761                     });
77762                 }, 600);  // after reveal.
77763
77764                 context.on('enter.intro', function(mode) {
77765                     if (!context.hasEntity(springStreetId)) {
77766                         return continueTo(searchStreet);
77767                     }
77768                     var ids = context.selectedIDs();
77769                     if (mode.id !== 'select' || !ids.length || ids[0] !== springStreetId) {
77770                         // keep Spring Street selected..
77771                         context.enter(modeSelect(context, [springStreetId]));
77772                     }
77773                 });
77774
77775                 context.history().on('change.intro', function() {
77776                     if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
77777                         timeout(function() {
77778                             continueTo(searchStreet);
77779                         }, 300);  // after any transition (e.g. if user deleted intersection)
77780                     }
77781                 });
77782
77783                 function continueTo(nextStep) {
77784                     context.map().on('move.intro drawn.intro', null);
77785                     context.on('enter.intro', null);
77786                     context.history().on('change.intro', null);
77787                     nextStep();
77788                 }
77789             }
77790
77791
77792             function editorStreet() {
77793                 var selector = '.entity-editor-pane button.close svg use';
77794                 var href = select(selector).attr('href') || '#iD-icon-close';
77795
77796                 reveal('.entity-editor-pane', helpString('intro.navigation.street_different_fields') + '{br}' +
77797                     helpString('intro.navigation.editor_street', {
77798                         button: icon(href, 'pre-text'),
77799                         field1: onewayField.label(),
77800                         field2: maxspeedField.label()
77801                     }));
77802
77803                 context.on('exit.intro', function() {
77804                     continueTo(play);
77805                 });
77806
77807                 context.history().on('change.intro', function() {
77808                     // update the close icon in the tooltip if the user edits something.
77809                     var selector = '.entity-editor-pane button.close svg use';
77810                     var href = select(selector).attr('href') || '#iD-icon-close';
77811
77812                     reveal('.entity-editor-pane', helpString('intro.navigation.street_different_fields') + '{br}' +
77813                         helpString('intro.navigation.editor_street', {
77814                             button: icon(href, 'pre-text'),
77815                             field1: onewayField.label(),
77816                             field2: maxspeedField.label()
77817                         }), { duration: 0 }
77818                     );
77819                 });
77820
77821                 function continueTo(nextStep) {
77822                     context.on('exit.intro', null);
77823                     context.history().on('change.intro', null);
77824                     nextStep();
77825                 }
77826             }
77827
77828
77829             function play() {
77830                 dispatch$1.call('done');
77831                 reveal('.ideditor',
77832                     helpString('intro.navigation.play', { next: _t('intro.points.title') }), {
77833                         tooltipBox: '.intro-nav-wrap .chapter-point',
77834                         buttonText: _t('intro.ok'),
77835                         buttonCallback: function() { reveal('.ideditor'); }
77836                     }
77837                 );
77838             }
77839
77840
77841             chapter.enter = function() {
77842                 dragMap();
77843             };
77844
77845
77846             chapter.exit = function() {
77847                 timeouts.forEach(window.clearTimeout);
77848                 context.on('enter.intro exit.intro', null);
77849                 context.map().on('move.intro drawn.intro', null);
77850                 context.history().on('change.intro', null);
77851                 context.container().select('.inspector-wrap').on('wheel.intro', null);
77852                 context.container().select('.search-header input').on('keydown.intro keyup.intro', null);
77853             };
77854
77855
77856             chapter.restart = function() {
77857                 chapter.exit();
77858                 chapter.enter();
77859             };
77860
77861
77862             return utilRebind(chapter, dispatch$1, 'on');
77863         }
77864
77865         function uiIntroPoint(context, reveal) {
77866             var dispatch$1 = dispatch('done');
77867             var timeouts = [];
77868             var intersection = [-85.63279, 41.94394];
77869             var building = [-85.632422, 41.944045];
77870             var cafePreset = _mainPresetIndex.item('amenity/cafe');
77871             var _pointID = null;
77872
77873
77874             var chapter = {
77875                 title: 'intro.points.title'
77876             };
77877
77878
77879             function timeout(f, t) {
77880                 timeouts.push(window.setTimeout(f, t));
77881             }
77882
77883
77884             function eventCancel() {
77885                 event.stopPropagation();
77886                 event.preventDefault();
77887             }
77888
77889
77890             function addPoint() {
77891                 context.enter(modeBrowse(context));
77892                 context.history().reset('initial');
77893
77894                 var msec = transitionTime(intersection, context.map().center());
77895                 if (msec) { reveal(null, null, { duration: 0 }); }
77896                 context.map().centerZoomEase(intersection, 19, msec);
77897
77898                 timeout(function() {
77899                     var tooltip = reveal('button.add-point',
77900                         helpString('intro.points.points_info') + '{br}' + helpString('intro.points.add_point'));
77901
77902                     _pointID = null;
77903
77904                     tooltip.selectAll('.popover-inner')
77905                         .insert('svg', 'span')
77906                         .attr('class', 'tooltip-illustration')
77907                         .append('use')
77908                         .attr('xlink:href', '#iD-graphic-points');
77909
77910                     context.on('enter.intro', function(mode) {
77911                         if (mode.id !== 'add-point') return;
77912                         continueTo(placePoint);
77913                     });
77914                 }, msec + 100);
77915
77916                 function continueTo(nextStep) {
77917                     context.on('enter.intro', null);
77918                     nextStep();
77919                 }
77920             }
77921
77922
77923             function placePoint() {
77924                 if (context.mode().id !== 'add-point') {
77925                     return chapter.restart();
77926                 }
77927
77928                 var pointBox = pad(building, 150, context);
77929                 var textId = context.lastPointerType() === 'mouse' ? 'place_point' : 'place_point_touch';
77930                 reveal(pointBox, helpString('intro.points.' + textId));
77931
77932                 context.map().on('move.intro drawn.intro', function() {
77933                     pointBox = pad(building, 150, context);
77934                     reveal(pointBox, helpString('intro.points.' + textId), { duration: 0 });
77935                 });
77936
77937                 context.on('enter.intro', function(mode) {
77938                     if (mode.id !== 'select') return chapter.restart();
77939                     _pointID = context.mode().selectedIDs()[0];
77940                     continueTo(searchPreset);
77941                 });
77942
77943                 function continueTo(nextStep) {
77944                     context.map().on('move.intro drawn.intro', null);
77945                     context.on('enter.intro', null);
77946                     nextStep();
77947                 }
77948             }
77949
77950
77951             function searchPreset() {
77952                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
77953                     return addPoint();
77954                 }
77955
77956                 // disallow scrolling
77957                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77958
77959                 context.container().select('.preset-search-input')
77960                     .on('keydown.intro', null)
77961                     .on('keyup.intro', checkPresetSearch);
77962
77963                 reveal('.preset-search-input',
77964                     helpString('intro.points.search_cafe', { preset: cafePreset.name() })
77965                 );
77966
77967                 context.on('enter.intro', function(mode) {
77968                     if (!_pointID || !context.hasEntity(_pointID)) {
77969                         return continueTo(addPoint);
77970                     }
77971
77972                     var ids = context.selectedIDs();
77973                     if (mode.id !== 'select' || !ids.length || ids[0] !== _pointID) {
77974                         // keep the user's point selected..
77975                         context.enter(modeSelect(context, [_pointID]));
77976
77977                         // disallow scrolling
77978                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77979
77980                         context.container().select('.preset-search-input')
77981                             .on('keydown.intro', null)
77982                             .on('keyup.intro', checkPresetSearch);
77983
77984                         reveal('.preset-search-input',
77985                             helpString('intro.points.search_cafe', { preset: cafePreset.name() })
77986                         );
77987
77988                         context.history().on('change.intro', null);
77989                     }
77990                 });
77991
77992
77993                 function checkPresetSearch() {
77994                     var first = context.container().select('.preset-list-item:first-child');
77995
77996                     if (first.classed('preset-amenity-cafe')) {
77997                         context.container().select('.preset-search-input')
77998                             .on('keydown.intro', eventCancel, true)
77999                             .on('keyup.intro', null);
78000
78001                         reveal(first.select('.preset-list-button').node(),
78002                             helpString('intro.points.choose_cafe', { preset: cafePreset.name() }),
78003                             { duration: 300 }
78004                         );
78005
78006                         context.history().on('change.intro', function() {
78007                             continueTo(aboutFeatureEditor);
78008                         });
78009                     }
78010                 }
78011
78012                 function continueTo(nextStep) {
78013                     context.on('enter.intro', null);
78014                     context.history().on('change.intro', null);
78015                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78016                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78017                     nextStep();
78018                 }
78019             }
78020
78021
78022             function aboutFeatureEditor() {
78023                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78024                     return addPoint();
78025                 }
78026
78027                 timeout(function() {
78028                     reveal('.entity-editor-pane', helpString('intro.points.feature_editor'), {
78029                         tooltipClass: 'intro-points-describe',
78030                         buttonText: _t('intro.ok'),
78031                         buttonCallback: function() { continueTo(addName); }
78032                     });
78033                 }, 400);
78034
78035                 context.on('exit.intro', function() {
78036                     // if user leaves select mode here, just continue with the tutorial.
78037                     continueTo(reselectPoint);
78038                 });
78039
78040                 function continueTo(nextStep) {
78041                     context.on('exit.intro', null);
78042                     nextStep();
78043                 }
78044             }
78045
78046
78047             function addName() {
78048                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78049                     return addPoint();
78050                 }
78051
78052                 // reset pane, in case user happened to change it..
78053                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78054
78055                 var addNameString = helpString('intro.points.fields_info') + '{br}' + helpString('intro.points.add_name');
78056
78057                 timeout(function() {
78058                     // It's possible for the user to add a name in a previous step..
78059                     // If so, don't tell them to add the name in this step.
78060                     // Give them an OK button instead.
78061                     var entity = context.entity(_pointID);
78062                     if (entity.tags.name) {
78063                         var tooltip = reveal('.entity-editor-pane', addNameString, {
78064                             tooltipClass: 'intro-points-describe',
78065                             buttonText: _t('intro.ok'),
78066                             buttonCallback: function() { continueTo(addCloseEditor); }
78067                         });
78068                         tooltip.select('.instruction').style('display', 'none');
78069
78070                     } else {
78071                         reveal('.entity-editor-pane', addNameString,
78072                             { tooltipClass: 'intro-points-describe' }
78073                         );
78074                     }
78075                 }, 400);
78076
78077                 context.history().on('change.intro', function() {
78078                     continueTo(addCloseEditor);
78079                 });
78080
78081                 context.on('exit.intro', function() {
78082                     // if user leaves select mode here, just continue with the tutorial.
78083                     continueTo(reselectPoint);
78084                 });
78085
78086                 function continueTo(nextStep) {
78087                     context.on('exit.intro', null);
78088                     context.history().on('change.intro', null);
78089                     nextStep();
78090                 }
78091             }
78092
78093
78094             function addCloseEditor() {
78095                 // reset pane, in case user happened to change it..
78096                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78097
78098                 var selector = '.entity-editor-pane button.close svg use';
78099                 var href = select(selector).attr('href') || '#iD-icon-close';
78100
78101                 context.on('exit.intro', function() {
78102                     continueTo(reselectPoint);
78103                 });
78104
78105                 reveal('.entity-editor-pane',
78106                     helpString('intro.points.add_close', { button: icon(href, 'pre-text') })
78107                 );
78108
78109                 function continueTo(nextStep) {
78110                     context.on('exit.intro', null);
78111                     nextStep();
78112                 }
78113             }
78114
78115
78116             function reselectPoint() {
78117                 if (!_pointID) return chapter.restart();
78118                 var entity = context.hasEntity(_pointID);
78119                 if (!entity) return chapter.restart();
78120
78121                 // make sure it's still a cafe, in case user somehow changed it..
78122                 var oldPreset = _mainPresetIndex.match(entity, context.graph());
78123                 context.replace(actionChangePreset(_pointID, oldPreset, cafePreset));
78124
78125                 context.enter(modeBrowse(context));
78126
78127                 var msec = transitionTime(entity.loc, context.map().center());
78128                 if (msec) { reveal(null, null, { duration: 0 }); }
78129                 context.map().centerEase(entity.loc, msec);
78130
78131                 timeout(function() {
78132                     var box = pointBox(entity.loc, context);
78133                     reveal(box, helpString('intro.points.reselect'), { duration: 600 });
78134
78135                     timeout(function() {
78136                         context.map().on('move.intro drawn.intro', function() {
78137                             var entity = context.hasEntity(_pointID);
78138                             if (!entity) return chapter.restart();
78139                             var box = pointBox(entity.loc, context);
78140                             reveal(box, helpString('intro.points.reselect'), { duration: 0 });
78141                         });
78142                     }, 600); // after reveal..
78143
78144                     context.on('enter.intro', function(mode) {
78145                         if (mode.id !== 'select') return;
78146                         continueTo(updatePoint);
78147                     });
78148
78149                 }, msec + 100);
78150
78151                 function continueTo(nextStep) {
78152                     context.map().on('move.intro drawn.intro', null);
78153                     context.on('enter.intro', null);
78154                     nextStep();
78155                 }
78156             }
78157
78158
78159             function updatePoint() {
78160                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78161                     return continueTo(reselectPoint);
78162                 }
78163
78164                 // reset pane, in case user happened to untag the point..
78165                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78166
78167                 context.on('exit.intro', function() {
78168                     continueTo(reselectPoint);
78169                 });
78170
78171                 context.history().on('change.intro', function() {
78172                     continueTo(updateCloseEditor);
78173                 });
78174
78175                 timeout(function() {
78176                     reveal('.entity-editor-pane', helpString('intro.points.update'),
78177                         { tooltipClass: 'intro-points-describe' }
78178                     );
78179                 }, 400);
78180
78181                 function continueTo(nextStep) {
78182                     context.on('exit.intro', null);
78183                     context.history().on('change.intro', null);
78184                     nextStep();
78185                 }
78186             }
78187
78188
78189             function updateCloseEditor() {
78190                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78191                     return continueTo(reselectPoint);
78192                 }
78193
78194                 // reset pane, in case user happened to change it..
78195                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78196
78197                 context.on('exit.intro', function() {
78198                     continueTo(rightClickPoint);
78199                 });
78200
78201                 timeout(function() {
78202                     reveal('.entity-editor-pane',
78203                         helpString('intro.points.update_close', { button: icon('#iD-icon-close', 'pre-text') })
78204                     );
78205                 }, 500);
78206
78207                 function continueTo(nextStep) {
78208                     context.on('exit.intro', null);
78209                     nextStep();
78210                 }
78211             }
78212
78213
78214             function rightClickPoint() {
78215                 if (!_pointID) return chapter.restart();
78216                 var entity = context.hasEntity(_pointID);
78217                 if (!entity) return chapter.restart();
78218
78219                 context.enter(modeBrowse(context));
78220
78221                 var box = pointBox(entity.loc, context);
78222                 var textId = context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch';
78223                 reveal(box, helpString('intro.points.' + textId), { duration: 600 });
78224
78225                 timeout(function() {
78226                     context.map().on('move.intro', function() {
78227                         var entity = context.hasEntity(_pointID);
78228                         if (!entity) return chapter.restart();
78229                         var box = pointBox(entity.loc, context);
78230                         reveal(box, helpString('intro.points.' + textId), { duration: 0 });
78231                     });
78232                 }, 600); // after reveal
78233
78234                 context.on('enter.intro', function(mode) {
78235                     if (mode.id !== 'select') return;
78236                     var ids = context.selectedIDs();
78237                     if (ids.length !== 1 || ids[0] !== _pointID) return;
78238
78239                     timeout(function() {
78240                         var node = selectMenuItem(context, 'delete').node();
78241                         if (!node) return;
78242                         continueTo(enterDelete);
78243                     }, 50);  // after menu visible
78244                 });
78245
78246                 function continueTo(nextStep) {
78247                     context.on('enter.intro', null);
78248                     context.map().on('move.intro', null);
78249                     nextStep();
78250                 }
78251             }
78252
78253
78254             function enterDelete() {
78255                 if (!_pointID) return chapter.restart();
78256                 var entity = context.hasEntity(_pointID);
78257                 if (!entity) return chapter.restart();
78258
78259                 var node = selectMenuItem(context, 'delete').node();
78260                 if (!node) { return continueTo(rightClickPoint); }
78261
78262                 reveal('.edit-menu',
78263                     helpString('intro.points.delete'),
78264                     { padding: 50 }
78265                 );
78266
78267                 timeout(function() {
78268                     context.map().on('move.intro', function() {
78269                         reveal('.edit-menu',
78270                             helpString('intro.points.delete'),
78271                             { duration: 0,  padding: 50 }
78272                         );
78273                     });
78274                 }, 300); // after menu visible
78275
78276                 context.on('exit.intro', function() {
78277                     if (!_pointID) return chapter.restart();
78278                     var entity = context.hasEntity(_pointID);
78279                     if (entity) return continueTo(rightClickPoint);  // point still exists
78280                 });
78281
78282                 context.history().on('change.intro', function(changed) {
78283                     if (changed.deleted().length) {
78284                         continueTo(undo);
78285                     }
78286                 });
78287
78288                 function continueTo(nextStep) {
78289                     context.map().on('move.intro', null);
78290                     context.history().on('change.intro', null);
78291                     context.on('exit.intro', null);
78292                     nextStep();
78293                 }
78294             }
78295
78296
78297             function undo() {
78298                 context.history().on('change.intro', function() {
78299                     continueTo(play);
78300                 });
78301
78302                 reveal('.top-toolbar button.undo-button',
78303                     helpString('intro.points.undo')
78304                 );
78305
78306                 function continueTo(nextStep) {
78307                     context.history().on('change.intro', null);
78308                     nextStep();
78309                 }
78310             }
78311
78312
78313             function play() {
78314                 dispatch$1.call('done');
78315                 reveal('.ideditor',
78316                     helpString('intro.points.play', { next: _t('intro.areas.title') }), {
78317                         tooltipBox: '.intro-nav-wrap .chapter-area',
78318                         buttonText: _t('intro.ok'),
78319                         buttonCallback: function() { reveal('.ideditor'); }
78320                     }
78321                 );
78322             }
78323
78324
78325             chapter.enter = function() {
78326                 addPoint();
78327             };
78328
78329
78330             chapter.exit = function() {
78331                 timeouts.forEach(window.clearTimeout);
78332                 context.on('enter.intro exit.intro', null);
78333                 context.map().on('move.intro drawn.intro', null);
78334                 context.history().on('change.intro', null);
78335                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78336                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78337             };
78338
78339
78340             chapter.restart = function() {
78341                 chapter.exit();
78342                 chapter.enter();
78343             };
78344
78345
78346             return utilRebind(chapter, dispatch$1, 'on');
78347         }
78348
78349         function uiIntroArea(context, reveal) {
78350             var dispatch$1 = dispatch('done');
78351             var playground = [-85.63552, 41.94159];
78352             var playgroundPreset = _mainPresetIndex.item('leisure/playground');
78353             var nameField = _mainPresetIndex.field('name');
78354             var descriptionField = _mainPresetIndex.field('description');
78355             var timeouts = [];
78356             var _areaID;
78357
78358
78359             var chapter = {
78360                 title: 'intro.areas.title'
78361             };
78362
78363
78364             function timeout(f, t) {
78365                 timeouts.push(window.setTimeout(f, t));
78366             }
78367
78368
78369             function eventCancel() {
78370                 event.stopPropagation();
78371                 event.preventDefault();
78372             }
78373
78374
78375             function revealPlayground(center, text, options) {
78376                 var padding = 180 * Math.pow(2, context.map().zoom() - 19.5);
78377                 var box = pad(center, padding, context);
78378                 reveal(box, text, options);
78379             }
78380
78381
78382             function addArea() {
78383                 context.enter(modeBrowse(context));
78384                 context.history().reset('initial');
78385                 _areaID = null;
78386
78387                 var msec = transitionTime(playground, context.map().center());
78388                 if (msec) { reveal(null, null, { duration: 0 }); }
78389                 context.map().centerZoomEase(playground, 19, msec);
78390
78391                 timeout(function() {
78392                     var tooltip = reveal('button.add-area',
78393                         helpString('intro.areas.add_playground'));
78394
78395                     tooltip.selectAll('.popover-inner')
78396                         .insert('svg', 'span')
78397                         .attr('class', 'tooltip-illustration')
78398                         .append('use')
78399                         .attr('xlink:href', '#iD-graphic-areas');
78400
78401                     context.on('enter.intro', function(mode) {
78402                         if (mode.id !== 'add-area') return;
78403                         continueTo(startPlayground);
78404                     });
78405                 }, msec + 100);
78406
78407                 function continueTo(nextStep) {
78408                     context.on('enter.intro', null);
78409                     nextStep();
78410                 }
78411             }
78412
78413
78414             function startPlayground() {
78415                 if (context.mode().id !== 'add-area') {
78416                     return chapter.restart();
78417                 }
78418
78419                 _areaID = null;
78420                 context.map().zoomEase(19.5, 500);
78421
78422                 timeout(function() {
78423                     var textId = context.lastPointerType() === 'mouse' ? 'starting_node_click' : 'starting_node_tap';
78424                     var startDrawString = helpString('intro.areas.start_playground') + helpString('intro.areas.' + textId);
78425                     revealPlayground(playground,
78426                         startDrawString, { duration: 250 }
78427                     );
78428
78429                     timeout(function() {
78430                         context.map().on('move.intro drawn.intro', function() {
78431                             revealPlayground(playground,
78432                                 startDrawString, { duration: 0 }
78433                             );
78434                         });
78435                         context.on('enter.intro', function(mode) {
78436                             if (mode.id !== 'draw-area') return chapter.restart();
78437                             continueTo(continuePlayground);
78438                         });
78439                     }, 250);  // after reveal
78440
78441                 }, 550);  // after easing
78442
78443                 function continueTo(nextStep) {
78444                     context.map().on('move.intro drawn.intro', null);
78445                     context.on('enter.intro', null);
78446                     nextStep();
78447                 }
78448             }
78449
78450
78451             function continuePlayground() {
78452                 if (context.mode().id !== 'draw-area') {
78453                     return chapter.restart();
78454                 }
78455
78456                 _areaID = null;
78457                 revealPlayground(playground,
78458                     helpString('intro.areas.continue_playground'),
78459                     { duration: 250 }
78460                 );
78461
78462                 timeout(function() {
78463                     context.map().on('move.intro drawn.intro', function() {
78464                         revealPlayground(playground,
78465                             helpString('intro.areas.continue_playground'),
78466                             { duration: 0 }
78467                         );
78468                     });
78469                 }, 250);  // after reveal
78470
78471                 context.on('enter.intro', function(mode) {
78472                     if (mode.id === 'draw-area') {
78473                         var entity = context.hasEntity(context.selectedIDs()[0]);
78474                         if (entity && entity.nodes.length >= 6) {
78475                             return continueTo(finishPlayground);
78476                         } else {
78477                             return;
78478                         }
78479                     } else if (mode.id === 'select') {
78480                         _areaID = context.selectedIDs()[0];
78481                         return continueTo(searchPresets);
78482                     } else {
78483                         return chapter.restart();
78484                     }
78485                 });
78486
78487                 function continueTo(nextStep) {
78488                     context.map().on('move.intro drawn.intro', null);
78489                     context.on('enter.intro', null);
78490                     nextStep();
78491                 }
78492             }
78493
78494
78495             function finishPlayground() {
78496                 if (context.mode().id !== 'draw-area') {
78497                     return chapter.restart();
78498                 }
78499
78500                 _areaID = null;
78501
78502                 var finishString = helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
78503                     helpString('intro.areas.finish_playground');
78504                 revealPlayground(playground,
78505                     finishString, { duration: 250 }
78506                 );
78507
78508                 timeout(function() {
78509                     context.map().on('move.intro drawn.intro', function() {
78510                         revealPlayground(playground,
78511                             finishString, { duration: 0 }
78512                         );
78513                     });
78514                 }, 250);  // after reveal
78515
78516                 context.on('enter.intro', function(mode) {
78517                     if (mode.id === 'draw-area') {
78518                         return;
78519                     } else if (mode.id === 'select') {
78520                         _areaID = context.selectedIDs()[0];
78521                         return continueTo(searchPresets);
78522                     } else {
78523                         return chapter.restart();
78524                     }
78525                 });
78526
78527                 function continueTo(nextStep) {
78528                     context.map().on('move.intro drawn.intro', null);
78529                     context.on('enter.intro', null);
78530                     nextStep();
78531                 }
78532             }
78533
78534
78535             function searchPresets() {
78536                 if (!_areaID || !context.hasEntity(_areaID)) {
78537                     return addArea();
78538                 }
78539                 var ids = context.selectedIDs();
78540                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78541                     context.enter(modeSelect(context, [_areaID]));
78542                 }
78543
78544                 // disallow scrolling
78545                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78546
78547                 timeout(function() {
78548                     // reset pane, in case user somehow happened to change it..
78549                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
78550
78551                     context.container().select('.preset-search-input')
78552                         .on('keydown.intro', null)
78553                         .on('keyup.intro', checkPresetSearch);
78554
78555                     reveal('.preset-search-input',
78556                         helpString('intro.areas.search_playground', { preset: playgroundPreset.name() })
78557                     );
78558                 }, 400);  // after preset list pane visible..
78559
78560                 context.on('enter.intro', function(mode) {
78561                     if (!_areaID || !context.hasEntity(_areaID)) {
78562                         return continueTo(addArea);
78563                     }
78564
78565                     var ids = context.selectedIDs();
78566                     if (mode.id !== 'select' || !ids.length || ids[0] !== _areaID) {
78567                         // keep the user's area selected..
78568                         context.enter(modeSelect(context, [_areaID]));
78569
78570                         // reset pane, in case user somehow happened to change it..
78571                         context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
78572                         // disallow scrolling
78573                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78574
78575                         context.container().select('.preset-search-input')
78576                             .on('keydown.intro', null)
78577                             .on('keyup.intro', checkPresetSearch);
78578
78579                         reveal('.preset-search-input',
78580                             helpString('intro.areas.search_playground', { preset: playgroundPreset.name() })
78581                         );
78582
78583                         context.history().on('change.intro', null);
78584                     }
78585                 });
78586
78587                 function checkPresetSearch() {
78588                     var first = context.container().select('.preset-list-item:first-child');
78589
78590                     if (first.classed('preset-leisure-playground')) {
78591                         reveal(first.select('.preset-list-button').node(),
78592                             helpString('intro.areas.choose_playground', { preset: playgroundPreset.name() }),
78593                             { duration: 300 }
78594                         );
78595
78596                         context.container().select('.preset-search-input')
78597                             .on('keydown.intro', eventCancel, true)
78598                             .on('keyup.intro', null);
78599
78600                         context.history().on('change.intro', function() {
78601                             continueTo(clickAddField);
78602                         });
78603                     }
78604                 }
78605
78606                 function continueTo(nextStep) {
78607                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78608                     context.on('enter.intro', null);
78609                     context.history().on('change.intro', null);
78610                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78611                     nextStep();
78612                 }
78613             }
78614
78615
78616             function clickAddField() {
78617                 if (!_areaID || !context.hasEntity(_areaID)) {
78618                     return addArea();
78619                 }
78620                 var ids = context.selectedIDs();
78621                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78622                     return searchPresets();
78623                 }
78624
78625                 if (!context.container().select('.form-field-description').empty()) {
78626                     return continueTo(describePlayground);
78627                 }
78628
78629                 // disallow scrolling
78630                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78631
78632                 timeout(function() {
78633                     // reset pane, in case user somehow happened to change it..
78634                     context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78635
78636                     // It's possible for the user to add a description in a previous step..
78637                     // If they did this already, just continue to next step.
78638                     var entity = context.entity(_areaID);
78639                     if (entity.tags.description) {
78640                         return continueTo(play);
78641                     }
78642
78643                     // scroll "Add field" into view
78644                     var box = context.container().select('.more-fields').node().getBoundingClientRect();
78645                     if (box.top > 300) {
78646                         var pane = context.container().select('.entity-editor-pane .inspector-body');
78647                         var start = pane.node().scrollTop;
78648                         var end = start + (box.top - 300);
78649
78650                         pane
78651                             .transition()
78652                             .duration(250)
78653                             .tween('scroll.inspector', function() {
78654                                 var node = this;
78655                                 var i = d3_interpolateNumber(start, end);
78656                                 return function(t) {
78657                                     node.scrollTop = i(t);
78658                                 };
78659                             });
78660                     }
78661
78662                     timeout(function() {
78663                         reveal('.more-fields .combobox-input',
78664                             helpString('intro.areas.add_field', {
78665                                 name: nameField.label(),
78666                                 description: descriptionField.label()
78667                             }),
78668                             { duration: 300 }
78669                         );
78670
78671                         context.container().select('.more-fields .combobox-input')
78672                             .on('click.intro', function() {
78673                                 // Watch for the combobox to appear...
78674                                 var watcher;
78675                                 watcher = window.setInterval(function() {
78676                                     if (!context.container().select('div.combobox').empty()) {
78677                                         window.clearInterval(watcher);
78678                                         continueTo(chooseDescriptionField);
78679                                     }
78680                                 }, 300);
78681                             });
78682                     }, 300);  // after "Add Field" visible
78683
78684                 }, 400);  // after editor pane visible
78685
78686                 context.on('exit.intro', function() {
78687                     return continueTo(searchPresets);
78688                 });
78689
78690                 function continueTo(nextStep) {
78691                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78692                     context.container().select('.more-fields .combobox-input').on('click.intro', null);
78693                     context.on('exit.intro', null);
78694                     nextStep();
78695                 }
78696             }
78697
78698
78699             function chooseDescriptionField() {
78700                 if (!_areaID || !context.hasEntity(_areaID)) {
78701                     return addArea();
78702                 }
78703                 var ids = context.selectedIDs();
78704                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78705                     return searchPresets();
78706                 }
78707
78708                 if (!context.container().select('.form-field-description').empty()) {
78709                     return continueTo(describePlayground);
78710                 }
78711
78712                 // Make sure combobox is ready..
78713                 if (context.container().select('div.combobox').empty()) {
78714                     return continueTo(clickAddField);
78715                 }
78716                 // Watch for the combobox to go away..
78717                 var watcher;
78718                 watcher = window.setInterval(function() {
78719                     if (context.container().select('div.combobox').empty()) {
78720                         window.clearInterval(watcher);
78721                         timeout(function() {
78722                             if (context.container().select('.form-field-description').empty()) {
78723                                 continueTo(retryChooseDescription);
78724                             } else {
78725                                 continueTo(describePlayground);
78726                             }
78727                         }, 300);  // after description field added.
78728                     }
78729                 }, 300);
78730
78731                 reveal('div.combobox',
78732                     helpString('intro.areas.choose_field', { field: descriptionField.label() }),
78733                     { duration: 300 }
78734                 );
78735
78736                 context.on('exit.intro', function() {
78737                     return continueTo(searchPresets);
78738                 });
78739
78740                 function continueTo(nextStep) {
78741                     if (watcher) window.clearInterval(watcher);
78742                     context.on('exit.intro', null);
78743                     nextStep();
78744                 }
78745             }
78746
78747
78748             function describePlayground() {
78749                 if (!_areaID || !context.hasEntity(_areaID)) {
78750                     return addArea();
78751                 }
78752                 var ids = context.selectedIDs();
78753                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78754                     return searchPresets();
78755                 }
78756
78757                 // reset pane, in case user happened to change it..
78758                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78759
78760                 if (context.container().select('.form-field-description').empty()) {
78761                     return continueTo(retryChooseDescription);
78762                 }
78763
78764                 context.on('exit.intro', function() {
78765                     continueTo(play);
78766                 });
78767
78768                 reveal('.entity-editor-pane',
78769                     helpString('intro.areas.describe_playground', { button: icon('#iD-icon-close', 'pre-text') }),
78770                     { duration: 300 }
78771                 );
78772
78773                 function continueTo(nextStep) {
78774                     context.on('exit.intro', null);
78775                     nextStep();
78776                 }
78777             }
78778
78779
78780             function retryChooseDescription() {
78781                 if (!_areaID || !context.hasEntity(_areaID)) {
78782                     return addArea();
78783                 }
78784                 var ids = context.selectedIDs();
78785                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78786                     return searchPresets();
78787                 }
78788
78789                 // reset pane, in case user happened to change it..
78790                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78791
78792                 reveal('.entity-editor-pane',
78793                     helpString('intro.areas.retry_add_field', { field: descriptionField.label() }), {
78794                     buttonText: _t('intro.ok'),
78795                     buttonCallback: function() { continueTo(clickAddField); }
78796                 });
78797
78798                 context.on('exit.intro', function() {
78799                     return continueTo(searchPresets);
78800                 });
78801
78802                 function continueTo(nextStep) {
78803                     context.on('exit.intro', null);
78804                     nextStep();
78805                 }
78806             }
78807
78808
78809             function play() {
78810                 dispatch$1.call('done');
78811                 reveal('.ideditor',
78812                     helpString('intro.areas.play', { next: _t('intro.lines.title') }), {
78813                         tooltipBox: '.intro-nav-wrap .chapter-line',
78814                         buttonText: _t('intro.ok'),
78815                         buttonCallback: function() { reveal('.ideditor'); }
78816                     }
78817                 );
78818             }
78819
78820
78821             chapter.enter = function() {
78822                 addArea();
78823             };
78824
78825
78826             chapter.exit = function() {
78827                 timeouts.forEach(window.clearTimeout);
78828                 context.on('enter.intro exit.intro', null);
78829                 context.map().on('move.intro drawn.intro', null);
78830                 context.history().on('change.intro', null);
78831                 context.container().select('.inspector-wrap').on('wheel.intro', null);
78832                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78833                 context.container().select('.more-fields .combobox-input').on('click.intro', null);
78834             };
78835
78836
78837             chapter.restart = function() {
78838                 chapter.exit();
78839                 chapter.enter();
78840             };
78841
78842
78843             return utilRebind(chapter, dispatch$1, 'on');
78844         }
78845
78846         function uiIntroLine(context, reveal) {
78847             var dispatch$1 = dispatch('done');
78848             var timeouts = [];
78849             var _tulipRoadID = null;
78850             var flowerRoadID = 'w646';
78851             var tulipRoadStart = [-85.6297754121684, 41.95805253325314];
78852             var tulipRoadMidpoint = [-85.62975395449628, 41.95787501510204];
78853             var tulipRoadIntersection = [-85.62974496187628, 41.95742515554585];
78854             var roadCategory = _mainPresetIndex.item('category-road_minor');
78855             var residentialPreset = _mainPresetIndex.item('highway/residential');
78856             var woodRoadID = 'w525';
78857             var woodRoadEndID = 'n2862';
78858             var woodRoadAddNode = [-85.62390110349587, 41.95397111462291];
78859             var woodRoadDragEndpoint = [-85.623867390213, 41.95466987786487];
78860             var woodRoadDragMidpoint = [-85.62386254803509, 41.95430395953872];
78861             var washingtonStreetID = 'w522';
78862             var twelfthAvenueID = 'w1';
78863             var eleventhAvenueEndID = 'n3550';
78864             var twelfthAvenueEndID = 'n5';
78865             var _washingtonSegmentID = null;
78866             var eleventhAvenueEnd = context.entity(eleventhAvenueEndID).loc;
78867             var twelfthAvenueEnd = context.entity(twelfthAvenueEndID).loc;
78868             var deleteLinesLoc = [-85.6219395542764, 41.95228033922477];
78869             var twelfthAvenue = [-85.62219310052491, 41.952505413152956];
78870
78871
78872             var chapter = {
78873                 title: 'intro.lines.title'
78874             };
78875
78876
78877             function timeout(f, t) {
78878                 timeouts.push(window.setTimeout(f, t));
78879             }
78880
78881
78882             function eventCancel() {
78883                 event.stopPropagation();
78884                 event.preventDefault();
78885             }
78886
78887
78888             function addLine() {
78889                 context.enter(modeBrowse(context));
78890                 context.history().reset('initial');
78891
78892                 var msec = transitionTime(tulipRoadStart, context.map().center());
78893                 if (msec) { reveal(null, null, { duration: 0 }); }
78894                 context.map().centerZoomEase(tulipRoadStart, 18.5, msec);
78895
78896                 timeout(function() {
78897                     var tooltip = reveal('button.add-line',
78898                         helpString('intro.lines.add_line'));
78899
78900                     tooltip.selectAll('.popover-inner')
78901                         .insert('svg', 'span')
78902                         .attr('class', 'tooltip-illustration')
78903                         .append('use')
78904                         .attr('xlink:href', '#iD-graphic-lines');
78905
78906                     context.on('enter.intro', function(mode) {
78907                         if (mode.id !== 'add-line') return;
78908                         continueTo(startLine);
78909                     });
78910                 }, msec + 100);
78911
78912                 function continueTo(nextStep) {
78913                     context.on('enter.intro', null);
78914                     nextStep();
78915                 }
78916             }
78917
78918
78919             function startLine() {
78920                 if (context.mode().id !== 'add-line') return chapter.restart();
78921
78922                 _tulipRoadID = null;
78923
78924                 var padding = 70 * Math.pow(2, context.map().zoom() - 18);
78925                 var box = pad(tulipRoadStart, padding, context);
78926                 box.height = box.height + 100;
78927
78928                 var textId = context.lastPointerType() === 'mouse' ? 'start_line' : 'start_line_tap';
78929                 var startLineString = helpString('intro.lines.missing_road') + '{br}' +
78930                     helpString('intro.lines.line_draw_info') +
78931                     helpString('intro.lines.' + textId);
78932                 reveal(box, startLineString);
78933
78934                 context.map().on('move.intro drawn.intro', function() {
78935                     padding = 70 * Math.pow(2, context.map().zoom() - 18);
78936                     box = pad(tulipRoadStart, padding, context);
78937                     box.height = box.height + 100;
78938                     reveal(box, startLineString, { duration: 0 });
78939                 });
78940
78941                 context.on('enter.intro', function(mode) {
78942                     if (mode.id !== 'draw-line') return chapter.restart();
78943                     continueTo(drawLine);
78944                 });
78945
78946                 function continueTo(nextStep) {
78947                     context.map().on('move.intro drawn.intro', null);
78948                     context.on('enter.intro', null);
78949                     nextStep();
78950                 }
78951             }
78952
78953
78954             function drawLine() {
78955                 if (context.mode().id !== 'draw-line') return chapter.restart();
78956
78957                 _tulipRoadID = context.mode().selectedIDs()[0];
78958                 context.map().centerEase(tulipRoadMidpoint, 500);
78959
78960                 timeout(function() {
78961                     var padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
78962                     var 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                     );
78967
78968                     context.map().on('move.intro drawn.intro', function() {
78969                         padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
78970                         box = pad(tulipRoadMidpoint, padding, context);
78971                         box.height = box.height * 2;
78972                         reveal(box,
78973                             helpString('intro.lines.intersect', { name: _t('intro.graph.name.flower-street') }),
78974                             { duration: 0 }
78975                         );
78976                     });
78977                 }, 550);  // after easing..
78978
78979                 context.history().on('change.intro', function() {
78980                     if (isLineConnected()) {
78981                         continueTo(continueLine);
78982                     }
78983                 });
78984
78985                 context.on('enter.intro', function(mode) {
78986                     if (mode.id === 'draw-line') {
78987                         return;
78988                     } else if (mode.id === 'select') {
78989                         continueTo(retryIntersect);
78990                         return;
78991                     } else {
78992                         return chapter.restart();
78993                     }
78994                 });
78995
78996                 function continueTo(nextStep) {
78997                     context.map().on('move.intro drawn.intro', null);
78998                     context.history().on('change.intro', null);
78999                     context.on('enter.intro', null);
79000                     nextStep();
79001                 }
79002             }
79003
79004
79005             function isLineConnected() {
79006                 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
79007                 if (!entity) return false;
79008
79009                 var drawNodes = context.graph().childNodes(entity);
79010                 return drawNodes.some(function(node) {
79011                     return context.graph().parentWays(node).some(function(parent) {
79012                         return parent.id === flowerRoadID;
79013                     });
79014                 });
79015             }
79016
79017
79018             function retryIntersect() {
79019                 select(window).on('pointerdown.intro mousedown.intro', eventCancel, true);
79020
79021                 var box = pad(tulipRoadIntersection, 80, context);
79022                 reveal(box,
79023                     helpString('intro.lines.retry_intersect', { name: _t('intro.graph.name.flower-street') })
79024                 );
79025
79026                 timeout(chapter.restart, 3000);
79027             }
79028
79029
79030             function continueLine() {
79031                 if (context.mode().id !== 'draw-line') return chapter.restart();
79032                 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
79033                 if (!entity) return chapter.restart();
79034
79035                 context.map().centerEase(tulipRoadIntersection, 500);
79036
79037                 var continueLineText = helpString('intro.lines.continue_line') + '{br}' +
79038                     helpString('intro.lines.finish_line_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
79039                     helpString('intro.lines.finish_road');
79040
79041                 reveal('.surface', continueLineText);
79042
79043                 context.on('enter.intro', function(mode) {
79044                     if (mode.id === 'draw-line')
79045                         return;
79046                     else if (mode.id === 'select')
79047                         return continueTo(chooseCategoryRoad);
79048                     else
79049                         return chapter.restart();
79050                 });
79051
79052                 function continueTo(nextStep) {
79053                     context.on('enter.intro', null);
79054                     nextStep();
79055                 }
79056             }
79057
79058
79059             function chooseCategoryRoad() {
79060                 if (context.mode().id !== 'select') return chapter.restart();
79061
79062                 context.on('exit.intro', function() {
79063                     return chapter.restart();
79064                 });
79065
79066                 var button = context.container().select('.preset-category-road_minor .preset-list-button');
79067                 if (button.empty()) return chapter.restart();
79068
79069                 // disallow scrolling
79070                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79071
79072                 timeout(function() {
79073                     // reset pane, in case user somehow happened to change it..
79074                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
79075
79076                     reveal(button.node(),
79077                         helpString('intro.lines.choose_category_road', { category: roadCategory.name() })
79078                     );
79079
79080                     button.on('click.intro', function() {
79081                         continueTo(choosePresetResidential);
79082                     });
79083
79084                 }, 400);  // after editor pane visible
79085
79086                 function continueTo(nextStep) {
79087                     context.container().select('.inspector-wrap').on('wheel.intro', null);
79088                     context.container().select('.preset-list-button').on('click.intro', null);
79089                     context.on('exit.intro', null);
79090                     nextStep();
79091                 }
79092             }
79093
79094
79095             function choosePresetResidential() {
79096                 if (context.mode().id !== 'select') return chapter.restart();
79097
79098                 context.on('exit.intro', function() {
79099                     return chapter.restart();
79100                 });
79101
79102                 var subgrid = context.container().select('.preset-category-road_minor .subgrid');
79103                 if (subgrid.empty()) return chapter.restart();
79104
79105                 subgrid.selectAll(':not(.preset-highway-residential) .preset-list-button')
79106                     .on('click.intro', function() {
79107                         continueTo(retryPresetResidential);
79108                     });
79109
79110                 subgrid.selectAll('.preset-highway-residential .preset-list-button')
79111                     .on('click.intro', function() {
79112                         continueTo(nameRoad);
79113                     });
79114
79115                 timeout(function() {
79116                     reveal(subgrid.node(),
79117                         helpString('intro.lines.choose_preset_residential', { preset: residentialPreset.name() }),
79118                         { tooltipBox: '.preset-highway-residential .preset-list-button', duration: 300 }
79119                     );
79120                 }, 300);
79121
79122                 function continueTo(nextStep) {
79123                     context.container().select('.preset-list-button').on('click.intro', null);
79124                     context.on('exit.intro', null);
79125                     nextStep();
79126                 }
79127             }
79128
79129
79130             // selected wrong road type
79131             function retryPresetResidential() {
79132                 if (context.mode().id !== 'select') return chapter.restart();
79133
79134                 context.on('exit.intro', function() {
79135                     return chapter.restart();
79136                 });
79137
79138                 // disallow scrolling
79139                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79140
79141                 timeout(function() {
79142                     var button = context.container().select('.entity-editor-pane .preset-list-button');
79143
79144                     reveal(button.node(),
79145                         helpString('intro.lines.retry_preset_residential', { preset: residentialPreset.name() })
79146                     );
79147
79148                     button.on('click.intro', function() {
79149                         continueTo(chooseCategoryRoad);
79150                     });
79151
79152                 }, 500);
79153
79154                 function continueTo(nextStep) {
79155                     context.container().select('.inspector-wrap').on('wheel.intro', null);
79156                     context.container().select('.preset-list-button').on('click.intro', null);
79157                     context.on('exit.intro', null);
79158                     nextStep();
79159                 }
79160             }
79161
79162
79163             function nameRoad() {
79164                 context.on('exit.intro', function() {
79165                     continueTo(didNameRoad);
79166                 });
79167
79168                 timeout(function() {
79169                     reveal('.entity-editor-pane',
79170                         helpString('intro.lines.name_road', { button: icon('#iD-icon-close', 'pre-text') }),
79171                         { tooltipClass: 'intro-lines-name_road' }
79172                     );
79173                 }, 500);
79174
79175                 function continueTo(nextStep) {
79176                     context.on('exit.intro', null);
79177                     nextStep();
79178                 }
79179             }
79180
79181
79182             function didNameRoad() {
79183                 context.history().checkpoint('doneAddLine');
79184
79185                 timeout(function() {
79186                     reveal('.surface', helpString('intro.lines.did_name_road'), {
79187                         buttonText: _t('intro.ok'),
79188                         buttonCallback: function() { continueTo(updateLine); }
79189                     });
79190                 }, 500);
79191
79192                 function continueTo(nextStep) {
79193                     nextStep();
79194                 }
79195             }
79196
79197
79198             function updateLine() {
79199                 context.history().reset('doneAddLine');
79200                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79201                     return chapter.restart();
79202                 }
79203
79204                 var msec = transitionTime(woodRoadDragMidpoint, context.map().center());
79205                 if (msec) { reveal(null, null, { duration: 0 }); }
79206                 context.map().centerZoomEase(woodRoadDragMidpoint, 19, msec);
79207
79208                 timeout(function() {
79209                     var padding = 250 * Math.pow(2, context.map().zoom() - 19);
79210                     var box = pad(woodRoadDragMidpoint, padding, context);
79211                     var advance = function() { continueTo(addNode); };
79212
79213                     reveal(box, helpString('intro.lines.update_line'),
79214                         { buttonText: _t('intro.ok'), buttonCallback: advance }
79215                     );
79216
79217                     context.map().on('move.intro drawn.intro', function() {
79218                         var padding = 250 * Math.pow(2, context.map().zoom() - 19);
79219                         var box = pad(woodRoadDragMidpoint, padding, context);
79220                         reveal(box, helpString('intro.lines.update_line'),
79221                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79222                         );
79223                     });
79224                 }, msec + 100);
79225
79226                 function continueTo(nextStep) {
79227                     context.map().on('move.intro drawn.intro', null);
79228                     nextStep();
79229                 }
79230             }
79231
79232
79233             function addNode() {
79234                 context.history().reset('doneAddLine');
79235                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79236                     return chapter.restart();
79237                 }
79238
79239                 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
79240                 var box = pad(woodRoadAddNode, padding, context);
79241                 var addNodeString = helpString('intro.lines.add_node' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
79242                 reveal(box, addNodeString);
79243
79244                 context.map().on('move.intro drawn.intro', function() {
79245                     var padding = 40 * Math.pow(2, context.map().zoom() - 19);
79246                     var box = pad(woodRoadAddNode, padding, context);
79247                     reveal(box, addNodeString, { duration: 0 });
79248                 });
79249
79250                 context.history().on('change.intro', function(changed) {
79251                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79252                         return continueTo(updateLine);
79253                     }
79254                     if (changed.created().length === 1) {
79255                         timeout(function() { continueTo(startDragEndpoint); }, 500);
79256                     }
79257                 });
79258
79259                 context.on('enter.intro', function(mode) {
79260                     if (mode.id !== 'select') {
79261                         continueTo(updateLine);
79262                     }
79263                 });
79264
79265                 function continueTo(nextStep) {
79266                     context.map().on('move.intro drawn.intro', null);
79267                     context.history().on('change.intro', null);
79268                     context.on('enter.intro', null);
79269                     nextStep();
79270                 }
79271             }
79272
79273
79274             function startDragEndpoint() {
79275                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79276                     return continueTo(updateLine);
79277                 }
79278                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79279                 var box = pad(woodRoadDragEndpoint, padding, context);
79280                 var startDragString = helpString('intro.lines.start_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch')) +
79281                     helpString('intro.lines.drag_to_intersection');
79282                 reveal(box, startDragString);
79283
79284                 context.map().on('move.intro drawn.intro', function() {
79285                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79286                         return continueTo(updateLine);
79287                     }
79288                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79289                     var box = pad(woodRoadDragEndpoint, padding, context);
79290                     reveal(box, startDragString, { duration: 0 });
79291
79292                     var entity = context.entity(woodRoadEndID);
79293                     if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) <= 4) {
79294                         continueTo(finishDragEndpoint);
79295                     }
79296                 });
79297
79298                 function continueTo(nextStep) {
79299                     context.map().on('move.intro drawn.intro', null);
79300                     nextStep();
79301                 }
79302             }
79303
79304
79305             function finishDragEndpoint() {
79306                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79307                     return continueTo(updateLine);
79308                 }
79309
79310                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79311                 var box = pad(woodRoadDragEndpoint, padding, context);
79312                 var finishDragString = helpString('intro.lines.spot_looks_good') +
79313                     helpString('intro.lines.finish_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
79314                 reveal(box, finishDragString);
79315
79316                 context.map().on('move.intro drawn.intro', function() {
79317                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79318                         return continueTo(updateLine);
79319                     }
79320                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79321                     var box = pad(woodRoadDragEndpoint, padding, context);
79322                     reveal(box, finishDragString, { duration: 0 });
79323
79324                     var entity = context.entity(woodRoadEndID);
79325                     if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) > 4) {
79326                         continueTo(startDragEndpoint);
79327                     }
79328                 });
79329
79330                 context.on('enter.intro', function() {
79331                     continueTo(startDragMidpoint);
79332                 });
79333
79334                 function continueTo(nextStep) {
79335                     context.map().on('move.intro drawn.intro', null);
79336                     context.on('enter.intro', null);
79337                     nextStep();
79338                 }
79339             }
79340
79341
79342             function startDragMidpoint() {
79343                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79344                     return continueTo(updateLine);
79345                 }
79346                 if (context.selectedIDs().indexOf(woodRoadID) === -1) {
79347                     context.enter(modeSelect(context, [woodRoadID]));
79348                 }
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'));
79353
79354                 context.map().on('move.intro drawn.intro', function() {
79355                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79356                         return continueTo(updateLine);
79357                     }
79358                     var padding = 80 * Math.pow(2, context.map().zoom() - 19);
79359                     var box = pad(woodRoadDragMidpoint, padding, context);
79360                     reveal(box, helpString('intro.lines.start_drag_midpoint'), { duration: 0 });
79361                 });
79362
79363                 context.history().on('change.intro', function(changed) {
79364                     if (changed.created().length === 1) {
79365                         continueTo(continueDragMidpoint);
79366                     }
79367                 });
79368
79369                 context.on('enter.intro', function(mode) {
79370                     if (mode.id !== 'select') {
79371                         // keep Wood Road selected so midpoint triangles are drawn..
79372                         context.enter(modeSelect(context, [woodRoadID]));
79373                     }
79374                 });
79375
79376                 function continueTo(nextStep) {
79377                     context.map().on('move.intro drawn.intro', null);
79378                     context.history().on('change.intro', null);
79379                     context.on('enter.intro', null);
79380                     nextStep();
79381                 }
79382             }
79383
79384
79385             function continueDragMidpoint() {
79386                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79387                     return continueTo(updateLine);
79388                 }
79389
79390                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79391                 var box = pad(woodRoadDragEndpoint, padding, context);
79392                 box.height += 400;
79393
79394                 var advance = function() {
79395                     context.history().checkpoint('doneUpdateLine');
79396                     continueTo(deleteLines);
79397                 };
79398
79399                 reveal(box, helpString('intro.lines.continue_drag_midpoint'),
79400                     { buttonText: _t('intro.ok'), buttonCallback: advance }
79401                 );
79402
79403                 context.map().on('move.intro drawn.intro', function() {
79404                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79405                         return continueTo(updateLine);
79406                     }
79407                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79408                     var box = pad(woodRoadDragEndpoint, padding, context);
79409                     box.height += 400;
79410                     reveal(box, helpString('intro.lines.continue_drag_midpoint'),
79411                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79412                     );
79413                 });
79414
79415                 function continueTo(nextStep) {
79416                     context.map().on('move.intro drawn.intro', null);
79417                     nextStep();
79418                 }
79419             }
79420
79421
79422             function deleteLines() {
79423                 context.history().reset('doneUpdateLine');
79424                 context.enter(modeBrowse(context));
79425
79426                 if (!context.hasEntity(washingtonStreetID) ||
79427                     !context.hasEntity(twelfthAvenueID) ||
79428                     !context.hasEntity(eleventhAvenueEndID)) {
79429                     return chapter.restart();
79430                 }
79431
79432                 var msec = transitionTime(deleteLinesLoc, context.map().center());
79433                 if (msec) { reveal(null, null, { duration: 0 }); }
79434                 context.map().centerZoomEase(deleteLinesLoc, 18, msec);
79435
79436                 timeout(function() {
79437                     var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79438                     var box = pad(deleteLinesLoc, padding, context);
79439                     box.top -= 200;
79440                     box.height += 400;
79441                     var advance = function() { continueTo(rightClickIntersection); };
79442
79443                     reveal(box, helpString('intro.lines.delete_lines', { street: _t('intro.graph.name.12th-avenue') }),
79444                         { buttonText: _t('intro.ok'), buttonCallback: advance }
79445                     );
79446
79447                     context.map().on('move.intro drawn.intro', function() {
79448                         var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79449                         var box = pad(deleteLinesLoc, padding, context);
79450                         box.top -= 200;
79451                         box.height += 400;
79452                         reveal(box, helpString('intro.lines.delete_lines', { street: _t('intro.graph.name.12th-avenue') }),
79453                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79454                         );
79455                     });
79456
79457                     context.history().on('change.intro', function() {
79458                         timeout(function() {
79459                             continueTo(deleteLines);
79460                         }, 500);  // after any transition (e.g. if user deleted intersection)
79461                     });
79462
79463                 }, msec + 100);
79464
79465                 function continueTo(nextStep) {
79466                     context.map().on('move.intro drawn.intro', null);
79467                     context.history().on('change.intro', null);
79468                     nextStep();
79469                 }
79470             }
79471
79472
79473             function rightClickIntersection() {
79474                 context.history().reset('doneUpdateLine');
79475                 context.enter(modeBrowse(context));
79476
79477                 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
79478
79479                 var rightClickString = helpString('intro.lines.split_street', {
79480                         street1: _t('intro.graph.name.11th-avenue'),
79481                         street2: _t('intro.graph.name.washington-street')
79482                     }) +
79483                     helpString('intro.lines.' + (context.lastPointerType() === 'mouse' ? 'rightclick_intersection' : 'edit_menu_intersection_touch'));
79484
79485                 timeout(function() {
79486                     var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79487                     var box = pad(eleventhAvenueEnd, padding, context);
79488                     reveal(box, rightClickString);
79489
79490                     context.map().on('move.intro drawn.intro', function() {
79491                         var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79492                         var box = pad(eleventhAvenueEnd, padding, context);
79493                         reveal(box, rightClickString,
79494                             { duration: 0 }
79495                         );
79496                     });
79497
79498                     context.on('enter.intro', function(mode) {
79499                         if (mode.id !== 'select') return;
79500                         var ids = context.selectedIDs();
79501                         if (ids.length !== 1 || ids[0] !== eleventhAvenueEndID) return;
79502
79503                         timeout(function() {
79504                             var node = selectMenuItem(context, 'split').node();
79505                             if (!node) return;
79506                             continueTo(splitIntersection);
79507                         }, 50);  // after menu visible
79508                     });
79509
79510                     context.history().on('change.intro', function() {
79511                         timeout(function() {
79512                             continueTo(deleteLines);
79513                         }, 300);  // after any transition (e.g. if user deleted intersection)
79514                     });
79515
79516                 }, 600);
79517
79518                 function continueTo(nextStep) {
79519                     context.map().on('move.intro drawn.intro', null);
79520                     context.on('enter.intro', null);
79521                     context.history().on('change.intro', null);
79522                     nextStep();
79523                 }
79524             }
79525
79526
79527             function splitIntersection() {
79528                 if (!context.hasEntity(washingtonStreetID) ||
79529                     !context.hasEntity(twelfthAvenueID) ||
79530                     !context.hasEntity(eleventhAvenueEndID)) {
79531                     return continueTo(deleteLines);
79532                 }
79533
79534                 var node = selectMenuItem(context, 'split').node();
79535                 if (!node) { return continueTo(rightClickIntersection); }
79536
79537                 var wasChanged = false;
79538                 _washingtonSegmentID = null;
79539
79540                 reveal('.edit-menu', helpString('intro.lines.split_intersection',
79541                     { street: _t('intro.graph.name.washington-street') }),
79542                     { padding: 50 }
79543                 );
79544
79545                 context.map().on('move.intro drawn.intro', function() {
79546                     var node = selectMenuItem(context, 'split').node();
79547                     if (!wasChanged && !node) { return continueTo(rightClickIntersection); }
79548
79549                     reveal('.edit-menu', helpString('intro.lines.split_intersection',
79550                         { street: _t('intro.graph.name.washington-street') }),
79551                         { duration: 0, padding: 50 }
79552                     );
79553                 });
79554
79555                 context.history().on('change.intro', function(changed) {
79556                     wasChanged = true;
79557                     timeout(function() {
79558                         if (context.history().undoAnnotation() === _t('operations.split.annotation.line')) {
79559                             _washingtonSegmentID = changed.created()[0].id;
79560                             continueTo(didSplit);
79561                         } else {
79562                             _washingtonSegmentID = null;
79563                             continueTo(retrySplit);
79564                         }
79565                     }, 300);  // after any transition (e.g. if user deleted intersection)
79566                 });
79567
79568                 function continueTo(nextStep) {
79569                     context.map().on('move.intro drawn.intro', null);
79570                     context.history().on('change.intro', null);
79571                     nextStep();
79572                 }
79573             }
79574
79575
79576             function retrySplit() {
79577                 context.enter(modeBrowse(context));
79578                 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
79579                 var advance = function() { continueTo(rightClickIntersection); };
79580
79581                 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79582                 var box = pad(eleventhAvenueEnd, padding, context);
79583                 reveal(box, helpString('intro.lines.retry_split'),
79584                     { buttonText: _t('intro.ok'), buttonCallback: advance }
79585                 );
79586
79587                 context.map().on('move.intro drawn.intro', function() {
79588                     var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79589                     var box = pad(eleventhAvenueEnd, padding, context);
79590                     reveal(box, helpString('intro.lines.retry_split'),
79591                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79592                     );
79593                 });
79594
79595                 function continueTo(nextStep) {
79596                     context.map().on('move.intro drawn.intro', null);
79597                     nextStep();
79598                 }
79599             }
79600
79601
79602             function didSplit() {
79603                 if (!_washingtonSegmentID ||
79604                     !context.hasEntity(_washingtonSegmentID) ||
79605                     !context.hasEntity(washingtonStreetID) ||
79606                     !context.hasEntity(twelfthAvenueID) ||
79607                     !context.hasEntity(eleventhAvenueEndID)) {
79608                     return continueTo(rightClickIntersection);
79609                 }
79610
79611                 var ids = context.selectedIDs();
79612                 var string = 'intro.lines.did_split_' + (ids.length > 1 ? 'multi' : 'single');
79613                 var street = _t('intro.graph.name.washington-street');
79614
79615                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79616                 var box = pad(twelfthAvenue, padding, context);
79617                 box.width = box.width / 2;
79618                 reveal(box, helpString(string, { street1: street, street2: street }),
79619                     { duration: 500 }
79620                 );
79621
79622                 timeout(function() {
79623                     context.map().centerZoomEase(twelfthAvenue, 18, 500);
79624
79625                     context.map().on('move.intro drawn.intro', function() {
79626                         var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79627                         var box = pad(twelfthAvenue, padding, context);
79628                         box.width = box.width / 2;
79629                         reveal(box, helpString(string, { street1: street, street2: street }),
79630                             { duration: 0 }
79631                         );
79632                     });
79633                 }, 600);  // after initial reveal and curtain cut
79634
79635                 context.on('enter.intro', function() {
79636                     var ids = context.selectedIDs();
79637                     if (ids.length === 1 && ids[0] === _washingtonSegmentID) {
79638                         continueTo(multiSelect);
79639                     }
79640                 });
79641
79642                 context.history().on('change.intro', function() {
79643                     if (!_washingtonSegmentID ||
79644                         !context.hasEntity(_washingtonSegmentID) ||
79645                         !context.hasEntity(washingtonStreetID) ||
79646                         !context.hasEntity(twelfthAvenueID) ||
79647                         !context.hasEntity(eleventhAvenueEndID)) {
79648                         return continueTo(rightClickIntersection);
79649                     }
79650                 });
79651
79652                 function continueTo(nextStep) {
79653                     context.map().on('move.intro drawn.intro', null);
79654                     context.on('enter.intro', null);
79655                     context.history().on('change.intro', null);
79656                     nextStep();
79657                 }
79658             }
79659
79660
79661             function multiSelect() {
79662                 if (!_washingtonSegmentID ||
79663                     !context.hasEntity(_washingtonSegmentID) ||
79664                     !context.hasEntity(washingtonStreetID) ||
79665                     !context.hasEntity(twelfthAvenueID) ||
79666                     !context.hasEntity(eleventhAvenueEndID)) {
79667                     return continueTo(rightClickIntersection);
79668                 }
79669
79670                 var ids = context.selectedIDs();
79671                 var hasWashington = ids.indexOf(_washingtonSegmentID) !== -1;
79672                 var hasTwelfth = ids.indexOf(twelfthAvenueID) !== -1;
79673
79674                 if (hasWashington && hasTwelfth) {
79675                     return continueTo(multiRightClick);
79676                 } else if (!hasWashington && !hasTwelfth) {
79677                     return continueTo(didSplit);
79678                 }
79679
79680                 context.map().centerZoomEase(twelfthAvenue, 18, 500);
79681
79682                 timeout(function() {
79683                     var selected, other, padding, box;
79684                     if (hasWashington) {
79685                         selected = _t('intro.graph.name.washington-street');
79686                         other = _t('intro.graph.name.12th-avenue');
79687                         padding = 60 * Math.pow(2, context.map().zoom() - 18);
79688                         box = pad(twelfthAvenueEnd, padding, context);
79689                         box.width *= 3;
79690                     } else {
79691                         selected = _t('intro.graph.name.12th-avenue');
79692                         other = _t('intro.graph.name.washington-street');
79693                         padding = 200 * Math.pow(2, context.map().zoom() - 18);
79694                         box = pad(twelfthAvenue, padding, context);
79695                         box.width /= 2;
79696                     }
79697
79698                     reveal(box,
79699                         helpString('intro.lines.multi_select',
79700                             { selected: selected, other1: other }) + ' ' +
79701                         helpString('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'),
79702                             { selected: selected, other2: other })
79703                     );
79704
79705                     context.map().on('move.intro drawn.intro', function() {
79706                         if (hasWashington) {
79707                             selected = _t('intro.graph.name.washington-street');
79708                             other = _t('intro.graph.name.12th-avenue');
79709                             padding = 60 * Math.pow(2, context.map().zoom() - 18);
79710                             box = pad(twelfthAvenueEnd, padding, context);
79711                             box.width *= 3;
79712                         } else {
79713                             selected = _t('intro.graph.name.12th-avenue');
79714                             other = _t('intro.graph.name.washington-street');
79715                             padding = 200 * Math.pow(2, context.map().zoom() - 18);
79716                             box = pad(twelfthAvenue, padding, context);
79717                             box.width /= 2;
79718                         }
79719
79720                         reveal(box,
79721                             helpString('intro.lines.multi_select',
79722                                 { selected: selected, other1: other }) + ' ' +
79723                             helpString('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'),
79724                                 { selected: selected, other2: other }),
79725                             { duration: 0 }
79726                         );
79727                     });
79728
79729                     context.on('enter.intro', function() {
79730                         continueTo(multiSelect);
79731                     });
79732
79733                     context.history().on('change.intro', function() {
79734                         if (!_washingtonSegmentID ||
79735                             !context.hasEntity(_washingtonSegmentID) ||
79736                             !context.hasEntity(washingtonStreetID) ||
79737                             !context.hasEntity(twelfthAvenueID) ||
79738                             !context.hasEntity(eleventhAvenueEndID)) {
79739                             return continueTo(rightClickIntersection);
79740                         }
79741                     });
79742                 }, 600);
79743
79744                 function continueTo(nextStep) {
79745                     context.map().on('move.intro drawn.intro', null);
79746                     context.on('enter.intro', null);
79747                     context.history().on('change.intro', null);
79748                     nextStep();
79749                 }
79750             }
79751
79752
79753             function multiRightClick() {
79754                 if (!_washingtonSegmentID ||
79755                     !context.hasEntity(_washingtonSegmentID) ||
79756                     !context.hasEntity(washingtonStreetID) ||
79757                     !context.hasEntity(twelfthAvenueID) ||
79758                     !context.hasEntity(eleventhAvenueEndID)) {
79759                     return continueTo(rightClickIntersection);
79760                 }
79761
79762                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79763                 var box = pad(twelfthAvenue, padding, context);
79764
79765                 var rightClickString = helpString('intro.lines.multi_select_success') +
79766                     helpString('intro.lines.multi_' + (context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch'));
79767                 reveal(box, rightClickString);
79768
79769                 context.map().on('move.intro drawn.intro', function() {
79770                     var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79771                     var box = pad(twelfthAvenue, padding, context);
79772                     reveal(box, rightClickString, { duration: 0 });
79773                 });
79774
79775                 context.ui().editMenu().on('toggled.intro', function(open) {
79776                     if (!open) return;
79777
79778                     timeout(function() {
79779                         var ids = context.selectedIDs();
79780                         if (ids.length === 2 &&
79781                             ids.indexOf(twelfthAvenueID) !== -1 &&
79782                             ids.indexOf(_washingtonSegmentID) !== -1) {
79783                                 var node = selectMenuItem(context, 'delete').node();
79784                                 if (!node) return;
79785                                 continueTo(multiDelete);
79786                         } else if (ids.length === 1 &&
79787                             ids.indexOf(_washingtonSegmentID) !== -1) {
79788                             return continueTo(multiSelect);
79789                         } else {
79790                             return continueTo(didSplit);
79791                         }
79792                     }, 300);  // after edit menu visible
79793                 });
79794
79795                 context.history().on('change.intro', function() {
79796                     if (!_washingtonSegmentID ||
79797                         !context.hasEntity(_washingtonSegmentID) ||
79798                         !context.hasEntity(washingtonStreetID) ||
79799                         !context.hasEntity(twelfthAvenueID) ||
79800                         !context.hasEntity(eleventhAvenueEndID)) {
79801                         return continueTo(rightClickIntersection);
79802                     }
79803                 });
79804
79805                 function continueTo(nextStep) {
79806                     context.map().on('move.intro drawn.intro', null);
79807                     context.ui().editMenu().on('toggled.intro', null);
79808                     context.history().on('change.intro', null);
79809                     nextStep();
79810                 }
79811             }
79812
79813
79814             function multiDelete() {
79815                 if (!_washingtonSegmentID ||
79816                     !context.hasEntity(_washingtonSegmentID) ||
79817                     !context.hasEntity(washingtonStreetID) ||
79818                     !context.hasEntity(twelfthAvenueID) ||
79819                     !context.hasEntity(eleventhAvenueEndID)) {
79820                     return continueTo(rightClickIntersection);
79821                 }
79822
79823                 var node = selectMenuItem(context, 'delete').node();
79824                 if (!node) return continueTo(multiRightClick);
79825
79826                 reveal('.edit-menu',
79827                     helpString('intro.lines.multi_delete'),
79828                     { padding: 50 }
79829                 );
79830
79831                 context.map().on('move.intro drawn.intro', function() {
79832                     reveal('.edit-menu',
79833                         helpString('intro.lines.multi_delete'),
79834                         { duration: 0, padding: 50 }
79835                     );
79836                 });
79837
79838                 context.on('exit.intro', function() {
79839                     if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
79840                         return continueTo(multiSelect);  // left select mode but roads still exist
79841                     }
79842                 });
79843
79844                 context.history().on('change.intro', function() {
79845                     if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
79846                         continueTo(retryDelete);         // changed something but roads still exist
79847                     } else {
79848                         continueTo(play);
79849                     }
79850                 });
79851
79852                 function continueTo(nextStep) {
79853                     context.map().on('move.intro drawn.intro', null);
79854                     context.on('exit.intro', null);
79855                     context.history().on('change.intro', null);
79856                     nextStep();
79857                 }
79858             }
79859
79860
79861             function retryDelete() {
79862                 context.enter(modeBrowse(context));
79863
79864                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79865                 var box = pad(twelfthAvenue, padding, context);
79866                 reveal(box, helpString('intro.lines.retry_delete'), {
79867                     buttonText: _t('intro.ok'),
79868                     buttonCallback: function() { continueTo(multiSelect); }
79869                 });
79870
79871                 function continueTo(nextStep) {
79872                     nextStep();
79873                 }
79874             }
79875
79876
79877             function play() {
79878                 dispatch$1.call('done');
79879                 reveal('.ideditor',
79880                     helpString('intro.lines.play', { next: _t('intro.buildings.title') }), {
79881                         tooltipBox: '.intro-nav-wrap .chapter-building',
79882                         buttonText: _t('intro.ok'),
79883                         buttonCallback: function() { reveal('.ideditor'); }
79884                     }
79885                 );
79886            }
79887
79888
79889             chapter.enter = function() {
79890                 addLine();
79891             };
79892
79893
79894             chapter.exit = function() {
79895                 timeouts.forEach(window.clearTimeout);
79896                 select(window).on('pointerdown.intro mousedown.intro', null, true);
79897                 context.on('enter.intro exit.intro', null);
79898                 context.map().on('move.intro drawn.intro', null);
79899                 context.history().on('change.intro', null);
79900                 context.container().select('.inspector-wrap').on('wheel.intro', null);
79901                 context.container().select('.preset-list-button').on('click.intro', null);
79902             };
79903
79904
79905             chapter.restart = function() {
79906                 chapter.exit();
79907                 chapter.enter();
79908             };
79909
79910
79911             return utilRebind(chapter, dispatch$1, 'on');
79912         }
79913
79914         function uiIntroBuilding(context, reveal) {
79915             var dispatch$1 = dispatch('done');
79916             var house = [-85.62815, 41.95638];
79917             var tank = [-85.62732, 41.95347];
79918             var buildingCatetory = _mainPresetIndex.item('category-building');
79919             var housePreset = _mainPresetIndex.item('building/house');
79920             var tankPreset = _mainPresetIndex.item('man_made/storage_tank');
79921             var timeouts = [];
79922             var _houseID = null;
79923             var _tankID = null;
79924
79925
79926             var chapter = {
79927                 title: 'intro.buildings.title'
79928             };
79929
79930
79931             function timeout(f, t) {
79932                 timeouts.push(window.setTimeout(f, t));
79933             }
79934
79935
79936             function eventCancel() {
79937                 event.stopPropagation();
79938                 event.preventDefault();
79939             }
79940
79941
79942             function revealHouse(center, text, options) {
79943                 var padding = 160 * Math.pow(2, context.map().zoom() - 20);
79944                 var box = pad(center, padding, context);
79945                 reveal(box, text, options);
79946             }
79947
79948
79949             function revealTank(center, text, options) {
79950                 var padding = 190 * Math.pow(2, context.map().zoom() - 19.5);
79951                 var box = pad(center, padding, context);
79952                 reveal(box, text, options);
79953             }
79954
79955
79956             function addHouse() {
79957                 context.enter(modeBrowse(context));
79958                 context.history().reset('initial');
79959                 _houseID = null;
79960
79961                 var msec = transitionTime(house, context.map().center());
79962                 if (msec) { reveal(null, null, { duration: 0 }); }
79963                 context.map().centerZoomEase(house, 19, msec);
79964
79965                 timeout(function() {
79966                     var tooltip = reveal('button.add-area',
79967                         helpString('intro.buildings.add_building'));
79968
79969                     tooltip.selectAll('.popover-inner')
79970                         .insert('svg', 'span')
79971                         .attr('class', 'tooltip-illustration')
79972                         .append('use')
79973                         .attr('xlink:href', '#iD-graphic-buildings');
79974
79975                     context.on('enter.intro', function(mode) {
79976                         if (mode.id !== 'add-area') return;
79977                         continueTo(startHouse);
79978                     });
79979                 }, msec + 100);
79980
79981                 function continueTo(nextStep) {
79982                     context.on('enter.intro', null);
79983                     nextStep();
79984                 }
79985             }
79986
79987
79988             function startHouse() {
79989                 if (context.mode().id !== 'add-area') {
79990                     return continueTo(addHouse);
79991                 }
79992
79993                 _houseID = null;
79994                 context.map().zoomEase(20, 500);
79995
79996                 timeout(function() {
79997                     var startString = helpString('intro.buildings.start_building') +
79998                         helpString('intro.buildings.building_corner_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
79999                     revealHouse(house, startString);
80000
80001                     context.map().on('move.intro drawn.intro', function() {
80002                         revealHouse(house, startString, { duration: 0 });
80003                     });
80004
80005                     context.on('enter.intro', function(mode) {
80006                         if (mode.id !== 'draw-area') return chapter.restart();
80007                         continueTo(continueHouse);
80008                     });
80009
80010                 }, 550);  // after easing
80011
80012                 function continueTo(nextStep) {
80013                     context.map().on('move.intro drawn.intro', null);
80014                     context.on('enter.intro', null);
80015                     nextStep();
80016                 }
80017             }
80018
80019
80020             function continueHouse() {
80021                 if (context.mode().id !== 'draw-area') {
80022                     return continueTo(addHouse);
80023                 }
80024
80025                 _houseID = null;
80026
80027                 var continueString = helpString('intro.buildings.continue_building') + '{br}' +
80028                     helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
80029                     helpString('intro.buildings.finish_building');
80030
80031                 revealHouse(house, continueString);
80032
80033                 context.map().on('move.intro drawn.intro', function() {
80034                     revealHouse(house, continueString, { duration: 0 });
80035                 });
80036
80037                 context.on('enter.intro', function(mode) {
80038                     if (mode.id === 'draw-area') {
80039                         return;
80040                     } else if (mode.id === 'select') {
80041                         var graph = context.graph();
80042                         var way = context.entity(context.selectedIDs()[0]);
80043                         var nodes = graph.childNodes(way);
80044                         var points = utilArrayUniq(nodes)
80045                             .map(function(n) { return context.projection(n.loc); });
80046
80047                         if (isMostlySquare(points)) {
80048                             _houseID = way.id;
80049                             return continueTo(chooseCategoryBuilding);
80050                         } else {
80051                             return continueTo(retryHouse);
80052                         }
80053
80054                     } else {
80055                         return chapter.restart();
80056                     }
80057                 });
80058
80059                 function continueTo(nextStep) {
80060                     context.map().on('move.intro drawn.intro', null);
80061                     context.on('enter.intro', null);
80062                     nextStep();
80063                 }
80064             }
80065
80066
80067             function retryHouse() {
80068                 var onClick = function() { continueTo(addHouse); };
80069
80070                 revealHouse(house, helpString('intro.buildings.retry_building'),
80071                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
80072                 );
80073
80074                 context.map().on('move.intro drawn.intro', function() {
80075                     revealHouse(house, helpString('intro.buildings.retry_building'),
80076                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
80077                     );
80078                 });
80079
80080                 function continueTo(nextStep) {
80081                     context.map().on('move.intro drawn.intro', null);
80082                     nextStep();
80083                 }
80084             }
80085
80086
80087             function chooseCategoryBuilding() {
80088                 if (!_houseID || !context.hasEntity(_houseID)) {
80089                     return addHouse();
80090                 }
80091                 var ids = context.selectedIDs();
80092                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80093                     context.enter(modeSelect(context, [_houseID]));
80094                 }
80095
80096                 // disallow scrolling
80097                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80098
80099                 timeout(function() {
80100                     // reset pane, in case user somehow happened to change it..
80101                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80102
80103                     var button = context.container().select('.preset-category-building .preset-list-button');
80104
80105                     reveal(button.node(),
80106                         helpString('intro.buildings.choose_category_building', { category: buildingCatetory.name() })
80107                     );
80108
80109                     button.on('click.intro', function() {
80110                         button.on('click.intro', null);
80111                         continueTo(choosePresetHouse);
80112                     });
80113
80114                 }, 400);  // after preset list pane visible..
80115
80116
80117                 context.on('enter.intro', function(mode) {
80118                     if (!_houseID || !context.hasEntity(_houseID)) {
80119                         return continueTo(addHouse);
80120                     }
80121                     var ids = context.selectedIDs();
80122                     if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
80123                         return continueTo(chooseCategoryBuilding);
80124                     }
80125                 });
80126
80127                 function continueTo(nextStep) {
80128                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80129                     context.container().select('.preset-list-button').on('click.intro', null);
80130                     context.on('enter.intro', null);
80131                     nextStep();
80132                 }
80133             }
80134
80135
80136             function choosePresetHouse() {
80137                 if (!_houseID || !context.hasEntity(_houseID)) {
80138                     return addHouse();
80139                 }
80140                 var ids = context.selectedIDs();
80141                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80142                     context.enter(modeSelect(context, [_houseID]));
80143                 }
80144
80145                 // disallow scrolling
80146                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80147
80148                 timeout(function() {
80149                     // reset pane, in case user somehow happened to change it..
80150                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80151
80152                     var button = context.container().select('.preset-building-house .preset-list-button');
80153
80154                     reveal(button.node(),
80155                         helpString('intro.buildings.choose_preset_house', { preset: housePreset.name() }),
80156                         { duration: 300 }
80157                     );
80158
80159                     button.on('click.intro', function() {
80160                         button.on('click.intro', null);
80161                         continueTo(closeEditorHouse);
80162                     });
80163
80164                 }, 400);  // after preset list pane visible..
80165
80166                 context.on('enter.intro', function(mode) {
80167                     if (!_houseID || !context.hasEntity(_houseID)) {
80168                         return continueTo(addHouse);
80169                     }
80170                     var ids = context.selectedIDs();
80171                     if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
80172                         return continueTo(chooseCategoryBuilding);
80173                     }
80174                 });
80175
80176                 function continueTo(nextStep) {
80177                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80178                     context.container().select('.preset-list-button').on('click.intro', null);
80179                     context.on('enter.intro', null);
80180                     nextStep();
80181                 }
80182             }
80183
80184
80185             function closeEditorHouse() {
80186                 if (!_houseID || !context.hasEntity(_houseID)) {
80187                     return addHouse();
80188                 }
80189                 var ids = context.selectedIDs();
80190                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80191                     context.enter(modeSelect(context, [_houseID]));
80192                 }
80193
80194                 context.history().checkpoint('hasHouse');
80195
80196                 context.on('exit.intro', function() {
80197                     continueTo(rightClickHouse);
80198                 });
80199
80200                 timeout(function() {
80201                     reveal('.entity-editor-pane',
80202                         helpString('intro.buildings.close', { button: icon('#iD-icon-close', 'pre-text') })
80203                     );
80204                 }, 500);
80205
80206                 function continueTo(nextStep) {
80207                     context.on('exit.intro', null);
80208                     nextStep();
80209                 }
80210             }
80211
80212
80213             function rightClickHouse() {
80214                 if (!_houseID) return chapter.restart();
80215
80216                 context.enter(modeBrowse(context));
80217                 context.history().reset('hasHouse');
80218                 var zoom = context.map().zoom();
80219                 if (zoom < 20) {
80220                     zoom = 20;
80221                 }
80222                 context.map().centerZoomEase(house, zoom, 500);
80223
80224                 context.on('enter.intro', function(mode) {
80225                     if (mode.id !== 'select') return;
80226                     var ids = context.selectedIDs();
80227                     if (ids.length !== 1 || ids[0] !== _houseID) return;
80228
80229                     timeout(function() {
80230                         var node = selectMenuItem(context, 'orthogonalize').node();
80231                         if (!node) return;
80232                         continueTo(clickSquare);
80233                     }, 50);  // after menu visible
80234                 });
80235
80236                 context.map().on('move.intro drawn.intro', function() {
80237                     var rightclickString = helpString('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_building' : 'edit_menu_building_touch'));
80238                     revealHouse(house, rightclickString, { duration: 0 });
80239                 });
80240
80241                 context.history().on('change.intro', function() {
80242                     continueTo(rightClickHouse);
80243                 });
80244
80245                 function continueTo(nextStep) {
80246                     context.on('enter.intro', null);
80247                     context.map().on('move.intro drawn.intro', null);
80248                     context.history().on('change.intro', null);
80249                     nextStep();
80250                 }
80251             }
80252
80253
80254             function clickSquare() {
80255                 if (!_houseID) return chapter.restart();
80256                 var entity = context.hasEntity(_houseID);
80257                 if (!entity) return continueTo(rightClickHouse);
80258
80259                 var node = selectMenuItem(context, 'orthogonalize').node();
80260                 if (!node) { return continueTo(rightClickHouse); }
80261
80262                 var wasChanged = false;
80263
80264                 reveal('.edit-menu',
80265                     helpString('intro.buildings.square_building'),
80266                     { padding: 50 }
80267                 );
80268
80269                 context.on('enter.intro', function(mode) {
80270                     if (mode.id === 'browse') {
80271                         continueTo(rightClickHouse);
80272                     } else if (mode.id === 'move' || mode.id === 'rotate') {
80273                         continueTo(retryClickSquare);
80274                     }
80275                 });
80276
80277                 context.map().on('move.intro', function() {
80278                     var node = selectMenuItem(context, 'orthogonalize').node();
80279                     if (!wasChanged && !node) { return continueTo(rightClickHouse); }
80280
80281                     reveal('.edit-menu',
80282                         helpString('intro.buildings.square_building'),
80283                         { duration: 0, padding: 50 }
80284                     );
80285                 });
80286
80287                 context.history().on('change.intro', function() {
80288                     wasChanged = true;
80289                     context.history().on('change.intro', null);
80290
80291                     // Something changed.  Wait for transition to complete and check undo annotation.
80292                     timeout(function() {
80293                         if (context.history().undoAnnotation() === _t('operations.orthogonalize.annotation.feature.single')) {
80294                             continueTo(doneSquare);
80295                         } else {
80296                             continueTo(retryClickSquare);
80297                         }
80298                     }, 500);  // after transitioned actions
80299                 });
80300
80301                 function continueTo(nextStep) {
80302                     context.on('enter.intro', null);
80303                     context.map().on('move.intro', null);
80304                     context.history().on('change.intro', null);
80305                     nextStep();
80306                 }
80307             }
80308
80309
80310             function retryClickSquare() {
80311                 context.enter(modeBrowse(context));
80312
80313                 revealHouse(house, helpString('intro.buildings.retry_square'), {
80314                     buttonText: _t('intro.ok'),
80315                     buttonCallback: function() { continueTo(rightClickHouse); }
80316                 });
80317
80318                 function continueTo(nextStep) {
80319                     nextStep();
80320                 }
80321             }
80322
80323
80324             function doneSquare() {
80325                 context.history().checkpoint('doneSquare');
80326
80327                 revealHouse(house, helpString('intro.buildings.done_square'), {
80328                     buttonText: _t('intro.ok'),
80329                     buttonCallback: function() { continueTo(addTank); }
80330                 });
80331
80332                 function continueTo(nextStep) {
80333                     nextStep();
80334                 }
80335             }
80336
80337
80338             function addTank() {
80339                 context.enter(modeBrowse(context));
80340                 context.history().reset('doneSquare');
80341                 _tankID = null;
80342
80343                 var msec = transitionTime(tank, context.map().center());
80344                 if (msec) { reveal(null, null, { duration: 0 }); }
80345                 context.map().centerZoomEase(tank, 19.5, msec);
80346
80347                 timeout(function() {
80348                     reveal('button.add-area',
80349                         helpString('intro.buildings.add_tank')
80350                     );
80351
80352                     context.on('enter.intro', function(mode) {
80353                         if (mode.id !== 'add-area') return;
80354                         continueTo(startTank);
80355                     });
80356                 }, msec + 100);
80357
80358                 function continueTo(nextStep) {
80359                     context.on('enter.intro', null);
80360                     nextStep();
80361                 }
80362             }
80363
80364
80365             function startTank() {
80366                 if (context.mode().id !== 'add-area') {
80367                     return continueTo(addTank);
80368                 }
80369
80370                 _tankID = null;
80371
80372                 timeout(function() {
80373                     var startString = helpString('intro.buildings.start_tank') +
80374                         helpString('intro.buildings.tank_edge_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
80375                     revealTank(tank, startString);
80376
80377                     context.map().on('move.intro drawn.intro', function() {
80378                         revealTank(tank, startString, { duration: 0 });
80379                     });
80380
80381                     context.on('enter.intro', function(mode) {
80382                         if (mode.id !== 'draw-area') return chapter.restart();
80383                         continueTo(continueTank);
80384                     });
80385
80386                 }, 550);  // after easing
80387
80388                 function continueTo(nextStep) {
80389                     context.map().on('move.intro drawn.intro', null);
80390                     context.on('enter.intro', null);
80391                     nextStep();
80392                 }
80393             }
80394
80395
80396             function continueTank() {
80397                 if (context.mode().id !== 'draw-area') {
80398                     return continueTo(addTank);
80399                 }
80400
80401                 _tankID = null;
80402
80403                 var continueString = helpString('intro.buildings.continue_tank') + '{br}' +
80404                     helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
80405                     helpString('intro.buildings.finish_tank');
80406
80407                 revealTank(tank, continueString);
80408
80409                 context.map().on('move.intro drawn.intro', function() {
80410                     revealTank(tank, continueString, { duration: 0 });
80411                 });
80412
80413                 context.on('enter.intro', function(mode) {
80414                     if (mode.id === 'draw-area') {
80415                         return;
80416                     } else if (mode.id === 'select') {
80417                         _tankID = context.selectedIDs()[0];
80418                         return continueTo(searchPresetTank);
80419                     } else {
80420                         return continueTo(addTank);
80421                     }
80422                 });
80423
80424                 function continueTo(nextStep) {
80425                     context.map().on('move.intro drawn.intro', null);
80426                     context.on('enter.intro', null);
80427                     nextStep();
80428                 }
80429             }
80430
80431
80432             function searchPresetTank() {
80433                 if (!_tankID || !context.hasEntity(_tankID)) {
80434                     return addTank();
80435                 }
80436                 var ids = context.selectedIDs();
80437                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
80438                     context.enter(modeSelect(context, [_tankID]));
80439                 }
80440
80441                 // disallow scrolling
80442                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80443
80444                 timeout(function() {
80445                     // reset pane, in case user somehow happened to change it..
80446                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80447
80448                     context.container().select('.preset-search-input')
80449                         .on('keydown.intro', null)
80450                         .on('keyup.intro', checkPresetSearch);
80451
80452                     reveal('.preset-search-input',
80453                         helpString('intro.buildings.search_tank', { preset: tankPreset.name() })
80454                     );
80455                 }, 400);  // after preset list pane visible..
80456
80457                 context.on('enter.intro', function(mode) {
80458                     if (!_tankID || !context.hasEntity(_tankID)) {
80459                         return continueTo(addTank);
80460                     }
80461
80462                     var ids = context.selectedIDs();
80463                     if (mode.id !== 'select' || !ids.length || ids[0] !== _tankID) {
80464                         // keep the user's area selected..
80465                         context.enter(modeSelect(context, [_tankID]));
80466
80467                         // reset pane, in case user somehow happened to change it..
80468                         context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80469                         // disallow scrolling
80470                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80471
80472                         context.container().select('.preset-search-input')
80473                             .on('keydown.intro', null)
80474                             .on('keyup.intro', checkPresetSearch);
80475
80476                         reveal('.preset-search-input',
80477                             helpString('intro.buildings.search_tank', { preset: tankPreset.name() })
80478                         );
80479
80480                         context.history().on('change.intro', null);
80481                     }
80482                 });
80483
80484                 function checkPresetSearch() {
80485                     var first = context.container().select('.preset-list-item:first-child');
80486
80487                     if (first.classed('preset-man_made-storage_tank')) {
80488                         reveal(first.select('.preset-list-button').node(),
80489                             helpString('intro.buildings.choose_tank', { preset: tankPreset.name() }),
80490                             { duration: 300 }
80491                         );
80492
80493                         context.container().select('.preset-search-input')
80494                             .on('keydown.intro', eventCancel, true)
80495                             .on('keyup.intro', null);
80496
80497                         context.history().on('change.intro', function() {
80498                             continueTo(closeEditorTank);
80499                         });
80500                     }
80501                 }
80502
80503                 function continueTo(nextStep) {
80504                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80505                     context.on('enter.intro', null);
80506                     context.history().on('change.intro', null);
80507                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80508                     nextStep();
80509                 }
80510             }
80511
80512
80513             function closeEditorTank() {
80514                 if (!_tankID || !context.hasEntity(_tankID)) {
80515                     return addTank();
80516                 }
80517                 var ids = context.selectedIDs();
80518                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
80519                     context.enter(modeSelect(context, [_tankID]));
80520                 }
80521
80522                 context.history().checkpoint('hasTank');
80523
80524                 context.on('exit.intro', function() {
80525                     continueTo(rightClickTank);
80526                 });
80527
80528                 timeout(function() {
80529                     reveal('.entity-editor-pane',
80530                         helpString('intro.buildings.close', { button: icon('#iD-icon-close', 'pre-text') })
80531                     );
80532                 }, 500);
80533
80534                 function continueTo(nextStep) {
80535                     context.on('exit.intro', null);
80536                     nextStep();
80537                 }
80538             }
80539
80540
80541             function rightClickTank() {
80542                 if (!_tankID) return continueTo(addTank);
80543
80544                 context.enter(modeBrowse(context));
80545                 context.history().reset('hasTank');
80546                 context.map().centerEase(tank, 500);
80547
80548                 timeout(function() {
80549                     context.on('enter.intro', function(mode) {
80550                         if (mode.id !== 'select') return;
80551                         var ids = context.selectedIDs();
80552                         if (ids.length !== 1 || ids[0] !== _tankID) return;
80553
80554                         timeout(function() {
80555                             var node = selectMenuItem(context, 'circularize').node();
80556                             if (!node) return;
80557                             continueTo(clickCircle);
80558                         }, 50);  // after menu visible
80559                     });
80560
80561                     var rightclickString = helpString('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_tank' : 'edit_menu_tank_touch'));
80562
80563                     revealTank(tank, rightclickString);
80564
80565                     context.map().on('move.intro drawn.intro', function() {
80566                         revealTank(tank, rightclickString, { duration: 0 });
80567                     });
80568
80569                     context.history().on('change.intro', function() {
80570                         continueTo(rightClickTank);
80571                     });
80572
80573                 }, 600);
80574
80575                 function continueTo(nextStep) {
80576                     context.on('enter.intro', null);
80577                     context.map().on('move.intro drawn.intro', null);
80578                     context.history().on('change.intro', null);
80579                     nextStep();
80580                 }
80581             }
80582
80583
80584             function clickCircle() {
80585                 if (!_tankID) return chapter.restart();
80586                 var entity = context.hasEntity(_tankID);
80587                 if (!entity) return continueTo(rightClickTank);
80588
80589                 var node = selectMenuItem(context, 'circularize').node();
80590                 if (!node) { return continueTo(rightClickTank); }
80591
80592                 var wasChanged = false;
80593
80594                 reveal('.edit-menu',
80595                     helpString('intro.buildings.circle_tank'),
80596                     { padding: 50 }
80597                 );
80598
80599                 context.on('enter.intro', function(mode) {
80600                     if (mode.id === 'browse') {
80601                         continueTo(rightClickTank);
80602                     } else if (mode.id === 'move' || mode.id === 'rotate') {
80603                         continueTo(retryClickCircle);
80604                     }
80605                 });
80606
80607                 context.map().on('move.intro', function() {
80608                     var node = selectMenuItem(context, 'circularize').node();
80609                     if (!wasChanged && !node) { return continueTo(rightClickTank); }
80610
80611                     reveal('.edit-menu',
80612                         helpString('intro.buildings.circle_tank'),
80613                         { duration: 0, padding: 50 }
80614                     );
80615                 });
80616
80617                 context.history().on('change.intro', function() {
80618                     wasChanged = true;
80619                     context.history().on('change.intro', null);
80620
80621                     // Something changed.  Wait for transition to complete and check undo annotation.
80622                     timeout(function() {
80623                         if (context.history().undoAnnotation() === _t('operations.circularize.annotation.single')) {
80624                             continueTo(play);
80625                         } else {
80626                             continueTo(retryClickCircle);
80627                         }
80628                     }, 500);  // after transitioned actions
80629                 });
80630
80631                 function continueTo(nextStep) {
80632                     context.on('enter.intro', null);
80633                     context.map().on('move.intro', null);
80634                     context.history().on('change.intro', null);
80635                     nextStep();
80636                 }
80637             }
80638
80639
80640             function retryClickCircle() {
80641                 context.enter(modeBrowse(context));
80642
80643                 revealTank(tank, helpString('intro.buildings.retry_circle'), {
80644                     buttonText: _t('intro.ok'),
80645                     buttonCallback: function() { continueTo(rightClickTank); }
80646                 });
80647
80648                 function continueTo(nextStep) {
80649                     nextStep();
80650                 }
80651             }
80652
80653
80654             function play() {
80655                 dispatch$1.call('done');
80656                 reveal('.ideditor',
80657                     helpString('intro.buildings.play', { next: _t('intro.startediting.title') }), {
80658                         tooltipBox: '.intro-nav-wrap .chapter-startEditing',
80659                         buttonText: _t('intro.ok'),
80660                         buttonCallback: function() { reveal('.ideditor'); }
80661                     }
80662                 );
80663             }
80664
80665
80666             chapter.enter = function() {
80667                 addHouse();
80668             };
80669
80670
80671             chapter.exit = function() {
80672                 timeouts.forEach(window.clearTimeout);
80673                 context.on('enter.intro exit.intro', null);
80674                 context.map().on('move.intro drawn.intro', null);
80675                 context.history().on('change.intro', null);
80676                 context.container().select('.inspector-wrap').on('wheel.intro', null);
80677                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80678                 context.container().select('.more-fields .combobox-input').on('click.intro', null);
80679             };
80680
80681
80682             chapter.restart = function() {
80683                 chapter.exit();
80684                 chapter.enter();
80685             };
80686
80687
80688             return utilRebind(chapter, dispatch$1, 'on');
80689         }
80690
80691         function uiIntroStartEditing(context, reveal) {
80692             var dispatch$1 = dispatch('done', 'startEditing');
80693             var modalSelection = select(null);
80694
80695
80696             var chapter = {
80697                 title: 'intro.startediting.title'
80698             };
80699
80700             function showHelp() {
80701                 reveal('.map-control.help-control',
80702                     helpString('intro.startediting.help'), {
80703                         buttonText: _t('intro.ok'),
80704                         buttonCallback: function() { shortcuts(); }
80705                     }
80706                 );
80707             }
80708
80709             function shortcuts() {
80710                 reveal('.map-control.help-control',
80711                     helpString('intro.startediting.shortcuts'), {
80712                         buttonText: _t('intro.ok'),
80713                         buttonCallback: function() { showSave(); }
80714                     }
80715                 );
80716             }
80717
80718             function showSave() {
80719                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80720                 reveal('.top-toolbar button.save',
80721                     helpString('intro.startediting.save'), {
80722                         buttonText: _t('intro.ok'),
80723                         buttonCallback: function() { showStart(); }
80724                     }
80725                 );
80726             }
80727
80728             function showStart() {
80729                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80730
80731                 modalSelection = uiModal(context.container());
80732
80733                 modalSelection.select('.modal')
80734                     .attr('class', 'modal-splash modal');
80735
80736                 modalSelection.selectAll('.close').remove();
80737
80738                 var startbutton = modalSelection.select('.content')
80739                     .attr('class', 'fillL')
80740                     .append('button')
80741                         .attr('class', 'modal-section huge-modal-button')
80742                         .on('click', function() {
80743                             modalSelection.remove();
80744                         });
80745
80746                     startbutton
80747                         .append('svg')
80748                         .attr('class', 'illustration')
80749                         .append('use')
80750                         .attr('xlink:href', '#iD-logo-walkthrough');
80751
80752                     startbutton
80753                         .append('h2')
80754                         .text(_t('intro.startediting.start'));
80755
80756                 dispatch$1.call('startEditing');
80757             }
80758
80759
80760             chapter.enter = function() {
80761                 showHelp();
80762             };
80763
80764
80765             chapter.exit = function() {
80766                 modalSelection.remove();
80767                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80768             };
80769
80770
80771             return utilRebind(chapter, dispatch$1, 'on');
80772         }
80773
80774         const chapterUi = {
80775           welcome: uiIntroWelcome,
80776           navigation: uiIntroNavigation,
80777           point: uiIntroPoint,
80778           area: uiIntroArea,
80779           line: uiIntroLine,
80780           building: uiIntroBuilding,
80781           startEditing: uiIntroStartEditing
80782         };
80783
80784         const chapterFlow = [
80785           'welcome',
80786           'navigation',
80787           'point',
80788           'area',
80789           'line',
80790           'building',
80791           'startEditing'
80792         ];
80793
80794
80795         function uiIntro(context) {
80796           const INTRO_IMAGERY = 'EsriWorldImageryClarity';
80797           let _introGraph = {};
80798           let _currChapter;
80799
80800
80801           function intro(selection) {
80802             _mainFileFetcher.get('intro_graph')
80803               .then(dataIntroGraph => {
80804                 // create entities for intro graph and localize names
80805                 for (let id in dataIntroGraph) {
80806                   if (!_introGraph[id]) {
80807                     _introGraph[id] = osmEntity(localize(dataIntroGraph[id]));
80808                   }
80809                 }
80810                 selection.call(startIntro);
80811               })
80812               .catch(function() { /* ignore */ });
80813           }
80814
80815
80816           function startIntro(selection) {
80817             context.enter(modeBrowse(context));
80818
80819             // Save current map state
80820             let osm = context.connection();
80821             let history = context.history().toJSON();
80822             let hash = window.location.hash;
80823             let center = context.map().center();
80824             let zoom = context.map().zoom();
80825             let background = context.background().baseLayerSource();
80826             let overlays = context.background().overlayLayerSources();
80827             let opacity = context.container().selectAll('.main-map .layer-background').style('opacity');
80828             let caches = osm && osm.caches();
80829             let baseEntities = context.history().graph().base().entities;
80830
80831             // Show sidebar and disable the sidebar resizing button
80832             // (this needs to be before `context.inIntro(true)`)
80833             context.ui().sidebar.expand();
80834             context.container().selectAll('button.sidebar-toggle').classed('disabled', true);
80835
80836             // Block saving
80837             context.inIntro(true);
80838
80839             // Load semi-real data used in intro
80840             if (osm) { osm.toggle(false).reset(); }
80841             context.history().reset();
80842             context.history().merge(Object.values(coreGraph().load(_introGraph).entities));
80843             context.history().checkpoint('initial');
80844
80845             // Setup imagery
80846             let imagery = context.background().findSource(INTRO_IMAGERY);
80847             if (imagery) {
80848               context.background().baseLayerSource(imagery);
80849             } else {
80850               context.background().bing();
80851             }
80852             overlays.forEach(d => context.background().toggleOverlayLayer(d));
80853
80854             // Setup data layers (only OSM)
80855             let layers = context.layers();
80856             layers.all().forEach(item => {
80857               // if the layer has the function `enabled`
80858               if (typeof item.layer.enabled === 'function') {
80859                 item.layer.enabled(item.id === 'osm');
80860               }
80861             });
80862
80863
80864             context.container().selectAll('.main-map .layer-background').style('opacity', 1);
80865
80866             let curtain = uiCurtain(context.container().node());
80867             selection.call(curtain);
80868
80869             // Store that the user started the walkthrough..
80870             corePreferences('walkthrough_started', 'yes');
80871
80872             // Restore previous walkthrough progress..
80873             let storedProgress = corePreferences('walkthrough_progress') || '';
80874             let progress = storedProgress.split(';').filter(Boolean);
80875
80876             let chapters = chapterFlow.map((chapter, i) => {
80877               let s = chapterUi[chapter](context, curtain.reveal)
80878                 .on('done', () => {
80879
80880                   buttons
80881                     .filter(d => d.title === s.title)
80882                     .classed('finished', true);
80883
80884                   if (i < chapterFlow.length - 1) {
80885                     const next = chapterFlow[i + 1];
80886                     context.container().select(`button.chapter-${next}`)
80887                       .classed('next', true);
80888                   }
80889
80890                   // Store walkthrough progress..
80891                   progress.push(chapter);
80892                   corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
80893                 });
80894               return s;
80895             });
80896
80897             chapters[chapters.length - 1].on('startEditing', () => {
80898               // Store walkthrough progress..
80899               progress.push('startEditing');
80900               corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
80901
80902               // Store if walkthrough is completed..
80903               let incomplete = utilArrayDifference(chapterFlow, progress);
80904               if (!incomplete.length) {
80905                 corePreferences('walkthrough_completed', 'yes');
80906               }
80907
80908               curtain.remove();
80909               navwrap.remove();
80910               context.container().selectAll('.main-map .layer-background').style('opacity', opacity);
80911               context.container().selectAll('button.sidebar-toggle').classed('disabled', false);
80912               if (osm) { osm.toggle(true).reset().caches(caches); }
80913               context.history().reset().merge(Object.values(baseEntities));
80914               context.background().baseLayerSource(background);
80915               overlays.forEach(d => context.background().toggleOverlayLayer(d));
80916               if (history) { context.history().fromJSON(history, false); }
80917               context.map().centerZoom(center, zoom);
80918               window.location.replace(hash);
80919               context.inIntro(false);
80920             });
80921
80922             let navwrap = selection
80923               .append('div')
80924               .attr('class', 'intro-nav-wrap fillD');
80925
80926             navwrap
80927               .append('svg')
80928               .attr('class', 'intro-nav-wrap-logo')
80929               .append('use')
80930               .attr('xlink:href', '#iD-logo-walkthrough');
80931
80932             let buttonwrap = navwrap
80933               .append('div')
80934               .attr('class', 'joined')
80935               .selectAll('button.chapter');
80936
80937             let buttons = buttonwrap
80938               .data(chapters)
80939               .enter()
80940               .append('button')
80941               .attr('class', (d, i) => `chapter chapter-${chapterFlow[i]}`)
80942               .on('click', enterChapter);
80943
80944             buttons
80945               .append('span')
80946               .text(d => _t(d.title));
80947
80948             buttons
80949               .append('span')
80950               .attr('class', 'status')
80951               .call(svgIcon((_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'));
80952
80953             enterChapter(chapters[0]);
80954
80955
80956             function enterChapter(newChapter) {
80957               if (_currChapter) { _currChapter.exit(); }
80958               context.enter(modeBrowse(context));
80959
80960               _currChapter = newChapter;
80961               _currChapter.enter();
80962
80963               buttons
80964                 .classed('next', false)
80965                 .classed('active', d => d.title === _currChapter.title);
80966             }
80967           }
80968
80969
80970           return intro;
80971         }
80972
80973         function uiIssuesInfo(context) {
80974
80975             var warningsItem = {
80976                 id: 'warnings',
80977                 count: 0,
80978                 iconID: 'iD-icon-alert',
80979                 descriptionID: 'issues.warnings_and_errors'
80980             };
80981
80982             var resolvedItem = {
80983                 id: 'resolved',
80984                 count: 0,
80985                 iconID: 'iD-icon-apply',
80986                 descriptionID: 'issues.user_resolved_issues'
80987             };
80988
80989             function update(selection) {
80990
80991                 var shownItems = [];
80992
80993                 var liveIssues = context.validator().getIssues({
80994                     what: corePreferences('validate-what') || 'edited',
80995                     where: corePreferences('validate-where') || 'all'
80996                 });
80997                 if (liveIssues.length) {
80998                     warningsItem.count = liveIssues.length;
80999                     shownItems.push(warningsItem);
81000                 }
81001
81002                 if (corePreferences('validate-what') === 'all') {
81003                     var resolvedIssues = context.validator().getResolvedIssues();
81004                     if (resolvedIssues.length) {
81005                         resolvedItem.count = resolvedIssues.length;
81006                         shownItems.push(resolvedItem);
81007                     }
81008                 }
81009
81010                 var chips = selection.selectAll('.chip')
81011                     .data(shownItems, function(d) {
81012                         return d.id;
81013                     });
81014
81015                 chips.exit().remove();
81016
81017                 var enter = chips.enter()
81018                     .append('a')
81019                     .attr('class', function(d) {
81020                         return 'chip ' + d.id + '-count';
81021                     })
81022                     .attr('href', '#')
81023                     .attr('tabindex', -1)
81024                     .each(function(d) {
81025
81026                         var chipSelection = select(this);
81027
81028                         var tooltipBehavior = uiTooltip()
81029                             .placement('top')
81030                             .title(_t(d.descriptionID));
81031
81032                         chipSelection
81033                             .call(tooltipBehavior)
81034                             .on('click', function() {
81035                                 event.preventDefault();
81036
81037                                 tooltipBehavior.hide(select(this));
81038                                 // open the Issues pane
81039                                 context.ui().togglePanes(context.container().select('.map-panes .issues-pane'));
81040                             });
81041
81042                         chipSelection.call(svgIcon('#' + d.iconID));
81043
81044                     });
81045
81046                 enter.append('span')
81047                     .attr('class', 'count');
81048
81049                 enter.merge(chips)
81050                     .selectAll('span.count')
81051                     .text(function(d) {
81052                         return d.count.toString();
81053                     });
81054             }
81055
81056
81057             return function(selection) {
81058                 update(selection);
81059
81060                 context.validator().on('validated.infobox', function() {
81061                     update(selection);
81062                 });
81063             };
81064         }
81065
81066         // import { utilGetDimensions } from '../util/dimensions';
81067
81068
81069         function uiMapInMap(context) {
81070
81071             function mapInMap(selection) {
81072                 var backgroundLayer = rendererTileLayer(context);
81073                 var overlayLayers = {};
81074                 var projection = geoRawMercator();
81075                 var dataLayer = svgData(projection, context).showLabels(false);
81076                 var debugLayer = svgDebug(projection, context);
81077                 var zoom = d3_zoom()
81078                     .scaleExtent([geoZoomToScale(0.5), geoZoomToScale(24)])
81079                     .on('start', zoomStarted)
81080                     .on('zoom', zoomed)
81081                     .on('end', zoomEnded);
81082
81083                 var wrap = select(null);
81084                 var tiles = select(null);
81085                 var viewport = select(null);
81086
81087                 var _isTransformed = false;
81088                 var _isHidden = true;
81089                 var _skipEvents = false;
81090                 var _gesture = null;
81091                 var _zDiff = 6;    // by default, minimap renders at (main zoom - 6)
81092                 var _dMini;        // dimensions of minimap
81093                 var _cMini;        // center pixel of minimap
81094                 var _tStart;       // transform at start of gesture
81095                 var _tCurr;        // transform at most recent event
81096                 var _timeoutID;
81097
81098
81099                 function zoomStarted() {
81100                     if (_skipEvents) return;
81101                     _tStart = _tCurr = projection.transform();
81102                     _gesture = null;
81103                 }
81104
81105
81106                 function zoomed() {
81107                     if (_skipEvents) return;
81108
81109                     var x = event.transform.x;
81110                     var y = event.transform.y;
81111                     var k = event.transform.k;
81112                     var isZooming = (k !== _tStart.k);
81113                     var isPanning = (x !== _tStart.x || y !== _tStart.y);
81114
81115                     if (!isZooming && !isPanning) {
81116                         return;  // no change
81117                     }
81118
81119                     // lock in either zooming or panning, don't allow both in minimap.
81120                     if (!_gesture) {
81121                         _gesture = isZooming ? 'zoom' : 'pan';
81122                     }
81123
81124                     var tMini = projection.transform();
81125                     var tX, tY, scale;
81126
81127                     if (_gesture === 'zoom') {
81128                         scale = k / tMini.k;
81129                         tX = (_cMini[0] / scale - _cMini[0]) * scale;
81130                         tY = (_cMini[1] / scale - _cMini[1]) * scale;
81131                     } else {
81132                         k = tMini.k;
81133                         scale = 1;
81134                         tX = x - tMini.x;
81135                         tY = y - tMini.y;
81136                     }
81137
81138                     utilSetTransform(tiles, tX, tY, scale);
81139                     utilSetTransform(viewport, 0, 0, scale);
81140                     _isTransformed = true;
81141                     _tCurr = identity$2.translate(x, y).scale(k);
81142
81143                     var zMain = geoScaleToZoom(context.projection.scale());
81144                     var zMini = geoScaleToZoom(k);
81145
81146                     _zDiff = zMain - zMini;
81147
81148                     queueRedraw();
81149                 }
81150
81151
81152                 function zoomEnded() {
81153                     if (_skipEvents) return;
81154                     if (_gesture !== 'pan') return;
81155
81156                     updateProjection();
81157                     _gesture = null;
81158                     context.map().center(projection.invert(_cMini));   // recenter main map..
81159                 }
81160
81161
81162                 function updateProjection() {
81163                     var loc = context.map().center();
81164                     var tMain = context.projection.transform();
81165                     var zMain = geoScaleToZoom(tMain.k);
81166                     var zMini = Math.max(zMain - _zDiff, 0.5);
81167                     var kMini = geoZoomToScale(zMini);
81168
81169                     projection
81170                         .translate([tMain.x, tMain.y])
81171                         .scale(kMini);
81172
81173                     var point = projection(loc);
81174                     var mouse = (_gesture === 'pan') ? geoVecSubtract([_tCurr.x, _tCurr.y], [_tStart.x, _tStart.y]) : [0, 0];
81175                     var xMini = _cMini[0] - point[0] + tMain.x + mouse[0];
81176                     var yMini = _cMini[1] - point[1] + tMain.y + mouse[1];
81177
81178                     projection
81179                         .translate([xMini, yMini])
81180                         .clipExtent([[0, 0], _dMini]);
81181
81182                     _tCurr = projection.transform();
81183
81184                     if (_isTransformed) {
81185                         utilSetTransform(tiles, 0, 0);
81186                         utilSetTransform(viewport, 0, 0);
81187                         _isTransformed = false;
81188                     }
81189
81190                     zoom
81191                         .scaleExtent([geoZoomToScale(0.5), geoZoomToScale(zMain - 3)]);
81192
81193                     _skipEvents = true;
81194                     wrap.call(zoom.transform, _tCurr);
81195                     _skipEvents = false;
81196                 }
81197
81198
81199                 function redraw() {
81200                     clearTimeout(_timeoutID);
81201                     if (_isHidden) return;
81202
81203                     updateProjection();
81204                     var zMini = geoScaleToZoom(projection.scale());
81205
81206                     // setup tile container
81207                     tiles = wrap
81208                         .selectAll('.map-in-map-tiles')
81209                         .data([0]);
81210
81211                     tiles = tiles.enter()
81212                         .append('div')
81213                         .attr('class', 'map-in-map-tiles')
81214                         .merge(tiles);
81215
81216                     // redraw background
81217                     backgroundLayer
81218                         .source(context.background().baseLayerSource())
81219                         .projection(projection)
81220                         .dimensions(_dMini);
81221
81222                     var background = tiles
81223                         .selectAll('.map-in-map-background')
81224                         .data([0]);
81225
81226                     background.enter()
81227                         .append('div')
81228                         .attr('class', 'map-in-map-background')
81229                         .merge(background)
81230                         .call(backgroundLayer);
81231
81232
81233                     // redraw overlay
81234                     var overlaySources = context.background().overlayLayerSources();
81235                     var activeOverlayLayers = [];
81236                     for (var i = 0; i < overlaySources.length; i++) {
81237                         if (overlaySources[i].validZoom(zMini)) {
81238                             if (!overlayLayers[i]) overlayLayers[i] = rendererTileLayer(context);
81239                             activeOverlayLayers.push(overlayLayers[i]
81240                                 .source(overlaySources[i])
81241                                 .projection(projection)
81242                                 .dimensions(_dMini));
81243                         }
81244                     }
81245
81246                     var overlay = tiles
81247                         .selectAll('.map-in-map-overlay')
81248                         .data([0]);
81249
81250                     overlay = overlay.enter()
81251                         .append('div')
81252                         .attr('class', 'map-in-map-overlay')
81253                         .merge(overlay);
81254
81255
81256                     var overlays = overlay
81257                         .selectAll('div')
81258                         .data(activeOverlayLayers, function(d) { return d.source().name(); });
81259
81260                     overlays.exit()
81261                         .remove();
81262
81263                     overlays = overlays.enter()
81264                         .append('div')
81265                         .merge(overlays)
81266                         .each(function(layer) { select(this).call(layer); });
81267
81268
81269                     var dataLayers = tiles
81270                         .selectAll('.map-in-map-data')
81271                         .data([0]);
81272
81273                     dataLayers.exit()
81274                         .remove();
81275
81276                     dataLayers = dataLayers.enter()
81277                         .append('svg')
81278                         .attr('class', 'map-in-map-data')
81279                         .merge(dataLayers)
81280                         .call(dataLayer)
81281                         .call(debugLayer);
81282
81283
81284                     // redraw viewport bounding box
81285                     if (_gesture !== 'pan') {
81286                         var getPath = d3_geoPath(projection);
81287                         var bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
81288
81289                         viewport = wrap.selectAll('.map-in-map-viewport')
81290                             .data([0]);
81291
81292                         viewport = viewport.enter()
81293                             .append('svg')
81294                             .attr('class', 'map-in-map-viewport')
81295                             .merge(viewport);
81296
81297
81298                         var path = viewport.selectAll('.map-in-map-bbox')
81299                             .data([bbox]);
81300
81301                         path.enter()
81302                             .append('path')
81303                             .attr('class', 'map-in-map-bbox')
81304                             .merge(path)
81305                             .attr('d', getPath)
81306                             .classed('thick', function(d) { return getPath.area(d) < 30; });
81307                     }
81308                 }
81309
81310
81311                 function queueRedraw() {
81312                     clearTimeout(_timeoutID);
81313                     _timeoutID = setTimeout(function() { redraw(); }, 750);
81314                 }
81315
81316
81317                 function toggle() {
81318                     if (event) event.preventDefault();
81319
81320                     _isHidden = !_isHidden;
81321
81322                     context.container().select('.minimap-toggle-item')
81323                         .classed('active', !_isHidden)
81324                         .select('input')
81325                         .property('checked', !_isHidden);
81326
81327                     if (_isHidden) {
81328                         wrap
81329                             .style('display', 'block')
81330                             .style('opacity', '1')
81331                             .transition()
81332                             .duration(200)
81333                             .style('opacity', '0')
81334                             .on('end', function() {
81335                                 selection.selectAll('.map-in-map')
81336                                     .style('display', 'none');
81337                             });
81338                     } else {
81339                         wrap
81340                             .style('display', 'block')
81341                             .style('opacity', '0')
81342                             .transition()
81343                             .duration(200)
81344                             .style('opacity', '1')
81345                             .on('end', function() {
81346                                 redraw();
81347                             });
81348                     }
81349                 }
81350
81351
81352                 uiMapInMap.toggle = toggle;
81353
81354                 wrap = selection.selectAll('.map-in-map')
81355                     .data([0]);
81356
81357                 wrap = wrap.enter()
81358                     .append('div')
81359                     .attr('class', 'map-in-map')
81360                     .style('display', (_isHidden ? 'none' : 'block'))
81361                     .call(zoom)
81362                     .on('dblclick.zoom', null)
81363                     .merge(wrap);
81364
81365                 // reflow warning: Hardcode dimensions - currently can't resize it anyway..
81366                 _dMini = [200,150]; //utilGetDimensions(wrap);
81367                 _cMini = geoVecScale(_dMini, 0.5);
81368
81369                 context.map()
81370                     .on('drawn.map-in-map', function(drawn) {
81371                         if (drawn.full === true) {
81372                             redraw();
81373                         }
81374                     });
81375
81376                 redraw();
81377
81378                 context.keybinding()
81379                     .on(_t('background.minimap.key'), toggle);
81380             }
81381
81382             return mapInMap;
81383         }
81384
81385         function uiNotice(context) {
81386
81387             return function(selection) {
81388                 var div = selection
81389                     .append('div')
81390                     .attr('class', 'notice');
81391
81392                 var button = div
81393                     .append('button')
81394                     .attr('class', 'zoom-to notice fillD')
81395                     .on('click', function() {
81396                         context.map().zoomEase(context.minEditableZoom());
81397                     })
81398                     .on('wheel', function() {   // let wheel events pass through #4482
81399                         var e2 = new WheelEvent(event.type, event);
81400                         context.surface().node().dispatchEvent(e2);
81401                     });
81402
81403                 button
81404                     .call(svgIcon('#iD-icon-plus', 'pre-text'))
81405                     .append('span')
81406                     .attr('class', 'label')
81407                     .text(_t('zoom_in_edit'));
81408
81409
81410                 function disableTooHigh() {
81411                     var canEdit = context.map().zoom() >= context.minEditableZoom();
81412                     div.style('display', canEdit ? 'none' : 'block');
81413                 }
81414
81415                 context.map()
81416                     .on('move.notice', debounce(disableTooHigh, 500));
81417
81418                 disableTooHigh();
81419             };
81420         }
81421
81422         function uiPhotoviewer(context) {
81423
81424             var dispatch$1 = dispatch('resize');
81425
81426             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
81427
81428             function photoviewer(selection) {
81429                 selection
81430                     .append('button')
81431                     .attr('class', 'thumb-hide')
81432                     .on('click', function () {
81433                         if (services.streetside) { services.streetside.hideViewer(context); }
81434                         if (services.mapillary) { services.mapillary.hideViewer(context); }
81435                         if (services.openstreetcam) { services.openstreetcam.hideViewer(context); }
81436                     })
81437                     .append('div')
81438                     .call(svgIcon('#iD-icon-close'));
81439
81440                 function preventDefault() {
81441                     event.preventDefault();
81442                 }
81443
81444                 selection
81445                     .append('button')
81446                     .attr('class', 'resize-handle-xy')
81447                     .on('touchstart touchdown touchend', preventDefault)
81448                     .on(
81449                         _pointerPrefix + 'down',
81450                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnX: true, resizeOnY: true })
81451                     );
81452
81453                 selection
81454                     .append('button')
81455                     .attr('class', 'resize-handle-x')
81456                     .on('touchstart touchdown touchend', preventDefault)
81457                     .on(
81458                         _pointerPrefix + 'down',
81459                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnX: true })
81460                     );
81461
81462                 selection
81463                     .append('button')
81464                     .attr('class', 'resize-handle-y')
81465                     .on('touchstart touchdown touchend', preventDefault)
81466                     .on(
81467                         _pointerPrefix + 'down',
81468                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnY: true })
81469                     );
81470
81471                 services.streetside.loadViewer(context);
81472                 services.mapillary.loadViewer(context);
81473                 services.openstreetcam.loadViewer(context);
81474
81475                 function buildResizeListener(target, eventName, dispatch, options) {
81476
81477                     var resizeOnX = !!options.resizeOnX;
81478                     var resizeOnY = !!options.resizeOnY;
81479                     var minHeight = options.minHeight || 240;
81480                     var minWidth = options.minWidth || 320;
81481                     var pointerId;
81482                     var startX;
81483                     var startY;
81484                     var startWidth;
81485                     var startHeight;
81486
81487                     function startResize() {
81488                         if (pointerId !== (event.pointerId || 'mouse')) return;
81489
81490                         event.preventDefault();
81491                         event.stopPropagation();
81492
81493                         var mapSize = context.map().dimensions();
81494
81495                         if (resizeOnX) {
81496                             var maxWidth = mapSize[0];
81497                             var newWidth = clamp((startWidth + event.clientX - startX), minWidth, maxWidth);
81498                             target.style('width', newWidth + 'px');
81499                         }
81500
81501                         if (resizeOnY) {
81502                             var maxHeight = mapSize[1] - 90;  // preserve space at top/bottom of map
81503                             var newHeight = clamp((startHeight + startY - event.clientY), minHeight, maxHeight);
81504                             target.style('height', newHeight + 'px');
81505                         }
81506
81507                         dispatch.call(eventName, target, utilGetDimensions(target, true));
81508                     }
81509
81510                     function clamp(num, min, max) {
81511                         return Math.max(min, Math.min(num, max));
81512                     }
81513
81514                     function stopResize() {
81515                         if (pointerId !== (event.pointerId || 'mouse')) return;
81516
81517                         event.preventDefault();
81518                         event.stopPropagation();
81519
81520                         // remove all the listeners we added
81521                         select(window)
81522                             .on('.' + eventName, null);
81523                     }
81524
81525                     return function initResize() {
81526                         event.preventDefault();
81527                         event.stopPropagation();
81528
81529                         pointerId = event.pointerId || 'mouse';
81530
81531                         startX = event.clientX;
81532                         startY = event.clientY;
81533                         var targetRect = target.node().getBoundingClientRect();
81534                         startWidth = targetRect.width;
81535                         startHeight = targetRect.height;
81536
81537                         select(window)
81538                             .on(_pointerPrefix + 'move.' + eventName, startResize, false)
81539                             .on(_pointerPrefix + 'up.' + eventName, stopResize, false);
81540
81541                         if (_pointerPrefix === 'pointer') {
81542                             select(window)
81543                                 .on('pointercancel.' + eventName, stopResize, false);
81544                         }
81545                     };
81546                 }
81547             }
81548
81549             photoviewer.onMapResize = function() {
81550                 var photoviewer = context.container().select('.photoviewer');
81551                 var content = context.container().select('.main-content');
81552                 var mapDimensions = utilGetDimensions(content, true);
81553                 // shrink photo viewer if it is too big
81554                 // (-90 preserves space at top and bottom of map used by menus)
81555                 var photoDimensions = utilGetDimensions(photoviewer, true);
81556                 if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > (mapDimensions[1] - 90)) {
81557                     var setPhotoDimensions = [
81558                         Math.min(photoDimensions[0], mapDimensions[0]),
81559                         Math.min(photoDimensions[1], mapDimensions[1] - 90),
81560                     ];
81561
81562                     photoviewer
81563                         .style('width', setPhotoDimensions[0] + 'px')
81564                         .style('height', setPhotoDimensions[1] + 'px');
81565
81566                     dispatch$1.call('resize', photoviewer, setPhotoDimensions);
81567                 }
81568             };
81569
81570             return utilRebind(photoviewer, dispatch$1, 'on');
81571         }
81572
81573         function uiRestore(context) {
81574           return function(selection) {
81575             if (!context.history().hasRestorableChanges()) return;
81576
81577             let modalSelection = uiModal(selection, true);
81578
81579             modalSelection.select('.modal')
81580               .attr('class', 'modal fillL');
81581
81582             let introModal = modalSelection.select('.content');
81583
81584             introModal
81585               .append('div')
81586               .attr('class', 'modal-section')
81587               .append('h3')
81588               .text(_t('restore.heading'));
81589
81590             introModal
81591               .append('div')
81592               .attr('class','modal-section')
81593               .append('p')
81594               .text(_t('restore.description'));
81595
81596             let buttonWrap = introModal
81597               .append('div')
81598               .attr('class', 'modal-actions');
81599
81600             let restore = buttonWrap
81601               .append('button')
81602               .attr('class', 'restore')
81603               .on('click', () => {
81604                 context.history().restore();
81605                 modalSelection.remove();
81606               });
81607
81608             restore
81609               .append('svg')
81610               .attr('class', 'logo logo-restore')
81611               .append('use')
81612               .attr('xlink:href', '#iD-logo-restore');
81613
81614             restore
81615               .append('div')
81616               .text(_t('restore.restore'));
81617
81618             let reset = buttonWrap
81619               .append('button')
81620               .attr('class', 'reset')
81621               .on('click', () => {
81622                 context.history().clearSaved();
81623                 modalSelection.remove();
81624               });
81625
81626             reset
81627               .append('svg')
81628               .attr('class', 'logo logo-reset')
81629               .append('use')
81630               .attr('xlink:href', '#iD-logo-reset');
81631
81632             reset
81633               .append('div')
81634               .text(_t('restore.reset'));
81635
81636             restore.node().focus();
81637           };
81638         }
81639
81640         function uiScale(context) {
81641             var projection = context.projection,
81642                 isImperial = !_mainLocalizer.usesMetric(),
81643                 maxLength = 180,
81644                 tickHeight = 8;
81645
81646
81647             function scaleDefs(loc1, loc2) {
81648                 var lat = (loc2[1] + loc1[1]) / 2,
81649                     conversion = (isImperial ? 3.28084 : 1),
81650                     dist = geoLonToMeters(loc2[0] - loc1[0], lat) * conversion,
81651                     scale = { dist: 0, px: 0, text: '' },
81652                     buckets, i, val, dLon;
81653
81654                 if (isImperial) {
81655                     buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
81656                 } else {
81657                     buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
81658                 }
81659
81660                 // determine a user-friendly endpoint for the scale
81661                 for (i = 0; i < buckets.length; i++) {
81662                     val = buckets[i];
81663                     if (dist >= val) {
81664                         scale.dist = Math.floor(dist / val) * val;
81665                         break;
81666                     } else {
81667                         scale.dist = +dist.toFixed(2);
81668                     }
81669                 }
81670
81671                 dLon = geoMetersToLon(scale.dist / conversion, lat);
81672                 scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
81673
81674                 scale.text = displayLength(scale.dist / conversion, isImperial);
81675
81676                 return scale;
81677             }
81678
81679
81680             function update(selection) {
81681                 // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
81682                 var dims = context.map().dimensions(),
81683                     loc1 = projection.invert([0, dims[1]]),
81684                     loc2 = projection.invert([maxLength, dims[1]]),
81685                     scale = scaleDefs(loc1, loc2);
81686
81687                 selection.select('.scale-path')
81688                     .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
81689
81690                 selection.select('.scale-textgroup')
81691                     .attr('transform', 'translate(' + (scale.px + 8) + ',' + tickHeight + ')');
81692
81693                 selection.select('.scale-text')
81694                     .text(scale.text);
81695             }
81696
81697
81698             return function(selection) {
81699                 function switchUnits() {
81700                     isImperial = !isImperial;
81701                     selection.call(update);
81702                 }
81703
81704                 var scalegroup = selection.append('svg')
81705                     .attr('class', 'scale')
81706                     .on('click', switchUnits)
81707                     .append('g')
81708                     .attr('transform', 'translate(10,11)');
81709
81710                 scalegroup
81711                     .append('path')
81712                     .attr('class', 'scale-path');
81713
81714                 scalegroup
81715                     .append('g')
81716                     .attr('class', 'scale-textgroup')
81717                     .append('text')
81718                     .attr('class', 'scale-text');
81719
81720                 selection.call(update);
81721
81722                 context.map().on('move.scale', function() {
81723                     update(selection);
81724                 });
81725             };
81726         }
81727
81728         function uiShortcuts(context) {
81729             var detected = utilDetect();
81730             var _activeTab = 0;
81731             var _modalSelection;
81732             var _selection = select(null);
81733
81734
81735             context.keybinding()
81736                 .on([_t('shortcuts.toggle.key'), '?'], function () {
81737                     if (context.container().selectAll('.modal-shortcuts').size()) {  // already showing
81738                         if (_modalSelection) {
81739                             _modalSelection.close();
81740                             _modalSelection = null;
81741                         }
81742                     } else {
81743                         _modalSelection = uiModal(_selection);
81744                         _modalSelection.call(shortcutsModal);
81745                     }
81746                 });
81747
81748
81749             function shortcutsModal(_modalSelection) {
81750                 _modalSelection.select('.modal')
81751                     .classed('modal-shortcuts', true);
81752
81753                 var content = _modalSelection.select('.content');
81754
81755                 content
81756                     .append('div')
81757                     .attr('class', 'modal-section')
81758                     .append('h3')
81759                     .text(_t('shortcuts.title'));
81760
81761                 _mainFileFetcher.get('shortcuts')
81762                     .then(function(data) { content.call(render, data); })
81763                     .catch(function() { /* ignore */ });
81764             }
81765
81766
81767             function render(selection, dataShortcuts) {
81768                 var wrapper = selection
81769                     .selectAll('.wrapper')
81770                     .data([0]);
81771
81772                 var wrapperEnter = wrapper
81773                     .enter()
81774                     .append('div')
81775                     .attr('class', 'wrapper modal-section');
81776
81777                 var tabsBar = wrapperEnter
81778                     .append('div')
81779                     .attr('class', 'tabs-bar');
81780
81781                 var shortcutsList = wrapperEnter
81782                     .append('div')
81783                     .attr('class', 'shortcuts-list');
81784
81785                 wrapper = wrapper.merge(wrapperEnter);
81786
81787                 var tabs = tabsBar
81788                     .selectAll('.tab')
81789                     .data(dataShortcuts);
81790
81791                 var tabsEnter = tabs
81792                     .enter()
81793                     .append('div')
81794                     .attr('class', 'tab')
81795                     .on('click', function (d, i) {
81796                         _activeTab = i;
81797                         render(selection, dataShortcuts);
81798                     });
81799
81800                 tabsEnter
81801                     .append('span')
81802                     .text(function (d) { return _t(d.text); });
81803
81804                 tabs = tabs
81805                     .merge(tabsEnter);
81806
81807                 // Update
81808                 wrapper.selectAll('.tab')
81809                     .classed('active', function (d, i) {
81810                         return i === _activeTab;
81811                     });
81812
81813
81814                 var shortcuts = shortcutsList
81815                     .selectAll('.shortcut-tab')
81816                     .data(dataShortcuts);
81817
81818                 var shortcutsEnter = shortcuts
81819                     .enter()
81820                     .append('div')
81821                     .attr('class', function(d) { return 'shortcut-tab shortcut-tab-' + d.tab; });
81822
81823                 var columnsEnter = shortcutsEnter
81824                     .selectAll('.shortcut-column')
81825                     .data(function (d) { return d.columns; })
81826                     .enter()
81827                     .append('table')
81828                     .attr('class', 'shortcut-column');
81829
81830                 var rowsEnter = columnsEnter
81831                     .selectAll('.shortcut-row')
81832                     .data(function (d) { return d.rows; })
81833                     .enter()
81834                     .append('tr')
81835                     .attr('class', 'shortcut-row');
81836
81837
81838                 var sectionRows = rowsEnter
81839                     .filter(function (d) { return !d.shortcuts; });
81840
81841                 sectionRows
81842                     .append('td');
81843
81844                 sectionRows
81845                     .append('td')
81846                     .attr('class', 'shortcut-section')
81847                     .append('h3')
81848                     .text(function (d) { return _t(d.text); });
81849
81850
81851                 var shortcutRows = rowsEnter
81852                     .filter(function (d) { return d.shortcuts; });
81853
81854                 var shortcutKeys = shortcutRows
81855                     .append('td')
81856                     .attr('class', 'shortcut-keys');
81857
81858                 var modifierKeys = shortcutKeys
81859                     .filter(function (d) { return d.modifiers; });
81860
81861                 modifierKeys
81862                     .selectAll('kbd.modifier')
81863                     .data(function (d) {
81864                         if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
81865                             return ['⌘'];
81866                         } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
81867                             return [];
81868                         } else {
81869                             return d.modifiers;
81870                         }
81871                     })
81872                     .enter()
81873                     .each(function () {
81874                         var selection = select(this);
81875
81876                         selection
81877                             .append('kbd')
81878                             .attr('class', 'modifier')
81879                             .text(function (d) { return uiCmd.display(d); });
81880
81881                         selection
81882                             .append('span')
81883                             .text('+');
81884                     });
81885
81886
81887                 shortcutKeys
81888                     .selectAll('kbd.shortcut')
81889                     .data(function (d) {
81890                         var arr = d.shortcuts;
81891                         if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
81892                             arr = ['Y'];
81893                         } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
81894                             arr = ['F11'];
81895                         }
81896
81897                         // replace translations
81898                         arr = arr.map(function(s) {
81899                             return uiCmd.display(s.indexOf('.') !== -1 ? _t(s) : s);
81900                         });
81901
81902                         return utilArrayUniq(arr).map(function(s) {
81903                             return {
81904                                 shortcut: s,
81905                                 separator: d.separator,
81906                                 suffix: d.suffix
81907                             };
81908                         });
81909                     })
81910                     .enter()
81911                     .each(function (d, i, nodes) {
81912                         var selection = select(this);
81913                         var click = d.shortcut.toLowerCase().match(/(.*).click/);
81914
81915                         if (click && click[1]) {   // replace "left_click", "right_click" with mouse icon
81916                             selection
81917                                 .call(svgIcon('#iD-walkthrough-mouse-' + click[1], 'operation'));
81918                         } else if (d.shortcut.toLowerCase() === 'long-press') {
81919                             selection
81920                                 .call(svgIcon('#iD-walkthrough-longpress', 'longpress operation'));
81921                         } else if (d.shortcut.toLowerCase() === 'tap') {
81922                             selection
81923                                 .call(svgIcon('#iD-walkthrough-tap', 'tap operation'));
81924                         } else {
81925                             selection
81926                                 .append('kbd')
81927                                 .attr('class', 'shortcut')
81928                                 .text(function (d) { return d.shortcut; });
81929                         }
81930
81931                         if (i < nodes.length - 1) {
81932                             selection
81933                                 .append('span')
81934                                 .text(d.separator || '\u00a0' + _t('shortcuts.or') + '\u00a0');
81935                         } else if (i === nodes.length - 1 && d.suffix) {
81936                             selection
81937                                 .append('span')
81938                                 .text(d.suffix);
81939                         }
81940                     });
81941
81942
81943                 shortcutKeys
81944                     .filter(function(d) { return d.gesture; })
81945                     .each(function () {
81946                         var selection = select(this);
81947
81948                         selection
81949                             .append('span')
81950                             .text('+');
81951
81952                         selection
81953                             .append('span')
81954                             .attr('class', 'gesture')
81955                             .text(function (d) { return _t(d.gesture); });
81956                     });
81957
81958
81959                 shortcutRows
81960                     .append('td')
81961                     .attr('class', 'shortcut-desc')
81962                     .text(function (d) { return d.text ? _t(d.text) : '\u00a0'; });
81963
81964
81965                 shortcuts = shortcuts
81966                     .merge(shortcutsEnter);
81967
81968                 // Update
81969                 wrapper.selectAll('.shortcut-tab')
81970                     .style('display', function (d, i) {
81971                         return i === _activeTab ? 'flex' : 'none';
81972                     });
81973             }
81974
81975
81976             return function(selection, show) {
81977                 _selection = selection;
81978                 if (show) {
81979                     _modalSelection = uiModal(selection);
81980                     _modalSelection.call(shortcutsModal);
81981                 }
81982             };
81983         }
81984
81985         var pair_1 = pair;
81986
81987
81988         function search(input, dims) {
81989           if (!dims) dims = 'NSEW';
81990           if (typeof input !== 'string') return null;
81991
81992           input = input.toUpperCase();
81993           var regex = /^[\s\,]*([NSEW])?\s*([\-|\—|\―]?[0-9.]+)[°º˚]?\s*(?:([0-9.]+)['’′‘]\s*)?(?:([0-9.]+)(?:''|"|”|″)\s*)?([NSEW])?/;
81994
81995           var m = input.match(regex);
81996           if (!m) return null;  // no match
81997
81998           var matched = m[0];
81999
82000           // extract dimension.. m[1] = leading, m[5] = trailing
82001           var dim;
82002           if (m[1] && m[5]) {                 // if matched both..
82003             dim = m[1];                       // keep leading
82004             matched = matched.slice(0, -1);   // remove trailing dimension from match
82005           } else {
82006             dim = m[1] || m[5];
82007           }
82008
82009           // if unrecognized dimension
82010           if (dim && dims.indexOf(dim) === -1) return null;
82011
82012           // extract DMS
82013           var deg = m[2] ? parseFloat(m[2]) : 0;
82014           var min = m[3] ? parseFloat(m[3]) / 60 : 0;
82015           var sec = m[4] ? parseFloat(m[4]) / 3600 : 0;
82016           var sign = (deg < 0) ? -1 : 1;
82017           if (dim === 'S' || dim === 'W') sign *= -1;
82018
82019           return {
82020             val: (Math.abs(deg) + min + sec) * sign,
82021             dim: dim,
82022             matched: matched,
82023             remain: input.slice(matched.length)
82024           };
82025         }
82026
82027
82028         function pair(input, dims) {
82029           input = input.trim();
82030           var one = search(input, dims);
82031           if (!one) return null;
82032
82033           input = one.remain.trim();
82034           var two = search(input, dims);
82035           if (!two || two.remain) return null;
82036
82037           if (one.dim) {
82038             return swapdim(one.val, two.val, one.dim);
82039           } else {
82040             return [one.val, two.val];
82041           }
82042         }
82043
82044
82045         function swapdim(a, b, dim) {
82046           if (dim === 'N' || dim === 'S') return [a, b];
82047           if (dim === 'W' || dim === 'E') return [b, a];
82048         }
82049
82050         function uiFeatureList(context) {
82051             var _geocodeResults;
82052
82053
82054             function featureList(selection) {
82055                 var header = selection
82056                     .append('div')
82057                     .attr('class', 'header fillL cf');
82058
82059                 header
82060                     .append('h3')
82061                     .text(_t('inspector.feature_list'));
82062
82063                 var searchWrap = selection
82064                     .append('div')
82065                     .attr('class', 'search-header');
82066
82067                 var search = searchWrap
82068                     .append('input')
82069                     .attr('placeholder', _t('inspector.search'))
82070                     .attr('type', 'search')
82071                     .call(utilNoAuto)
82072                     .on('keypress', keypress)
82073                     .on('keydown', keydown)
82074                     .on('input', inputevent);
82075
82076                 searchWrap
82077                     .call(svgIcon('#iD-icon-search', 'pre-text'));
82078
82079                 var listWrap = selection
82080                     .append('div')
82081                     .attr('class', 'inspector-body');
82082
82083                 var list = listWrap
82084                     .append('div')
82085                     .attr('class', 'feature-list cf');
82086
82087                 context
82088                     .on('exit.feature-list', clearSearch);
82089                 context.map()
82090                     .on('drawn.feature-list', mapDrawn);
82091
82092                 context.keybinding()
82093                     .on(uiCmd('⌘F'), focusSearch);
82094
82095
82096                 function focusSearch() {
82097                     var mode = context.mode() && context.mode().id;
82098                     if (mode !== 'browse') return;
82099
82100                     event.preventDefault();
82101                     search.node().focus();
82102                 }
82103
82104
82105                 function keydown() {
82106                     if (event.keyCode === 27) {  // escape
82107                         search.node().blur();
82108                     }
82109                 }
82110
82111
82112                 function keypress() {
82113                     var q = search.property('value'),
82114                         items = list.selectAll('.feature-list-item');
82115                     if (event.keyCode === 13 && q.length && items.size()) {  // return
82116                         click(items.datum());
82117                     }
82118                 }
82119
82120
82121                 function inputevent() {
82122                     _geocodeResults = undefined;
82123                     drawList();
82124                 }
82125
82126
82127                 function clearSearch() {
82128                     search.property('value', '');
82129                     drawList();
82130                 }
82131
82132
82133                 function mapDrawn(e) {
82134                     if (e.full) {
82135                         drawList();
82136                     }
82137                 }
82138
82139
82140                 function features() {
82141                     var result = [];
82142                     var graph = context.graph();
82143                     var visibleCenter = context.map().extent().center();
82144                     var q = search.property('value').toLowerCase();
82145
82146                     if (!q) return result;
82147
82148                     var idMatch = q.match(/(?:^|\W)(node|way|relation|[nwr])\W?0*([1-9]\d*)(?:\W|$)/i);
82149
82150                     if (idMatch) {
82151                         var elemType = idMatch[1].charAt(0);
82152                         var elemId = idMatch[2];
82153                         result.push({
82154                             id: elemType + elemId,
82155                             geometry: elemType === 'n' ? 'point' : elemType === 'w' ? 'line' : 'relation',
82156                             type: elemType === 'n' ? _t('inspector.node') : elemType === 'w' ? _t('inspector.way') : _t('inspector.relation'),
82157                             name: elemId
82158                         });
82159                     }
82160
82161                     var locationMatch = pair_1(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
82162
82163                     if (locationMatch) {
82164                         var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
82165                         result.push({
82166                             id: -1,
82167                             geometry: 'point',
82168                             type: _t('inspector.location'),
82169                             name: dmsCoordinatePair([loc[1], loc[0]]),
82170                             location: loc
82171                         });
82172                     }
82173
82174                     var allEntities = graph.entities;
82175                     var localResults = [];
82176                     for (var id in allEntities) {
82177                         var entity = allEntities[id];
82178                         if (!entity) continue;
82179
82180                         var name = utilDisplayName(entity) || '';
82181                         if (name.toLowerCase().indexOf(q) < 0) continue;
82182
82183                         var matched = _mainPresetIndex.match(entity, graph);
82184                         var type = (matched && matched.name()) || utilDisplayType(entity.id);
82185                         var extent = entity.extent(graph);
82186                         var distance = extent ? geoSphericalDistance(visibleCenter, extent.center()) : 0;
82187
82188                         localResults.push({
82189                             id: entity.id,
82190                             entity: entity,
82191                             geometry: entity.geometry(graph),
82192                             type: type,
82193                             name: name,
82194                             distance: distance
82195                         });
82196
82197                         if (localResults.length > 100) break;
82198                     }
82199                     localResults = localResults.sort(function byDistance(a, b) {
82200                         return a.distance - b.distance;
82201                     });
82202                     result = result.concat(localResults);
82203
82204                     (_geocodeResults || []).forEach(function(d) {
82205                         if (d.osm_type && d.osm_id) {    // some results may be missing these - #1890
82206
82207                             // Make a temporary osmEntity so we can preset match
82208                             // and better localize the search result - #4725
82209                             var id = osmEntity.id.fromOSM(d.osm_type, d.osm_id);
82210                             var tags = {};
82211                             tags[d.class] = d.type;
82212
82213                             var attrs = { id: id, type: d.osm_type, tags: tags };
82214                             if (d.osm_type === 'way') {   // for ways, add some fake closed nodes
82215                                 attrs.nodes = ['a','a'];  // so that geometry area is possible
82216                             }
82217
82218                             var tempEntity = osmEntity(attrs);
82219                             var tempGraph = coreGraph([tempEntity]);
82220                             var matched = _mainPresetIndex.match(tempEntity, tempGraph);
82221                             var type = (matched && matched.name()) || utilDisplayType(id);
82222
82223                             result.push({
82224                                 id: tempEntity.id,
82225                                 geometry: tempEntity.geometry(tempGraph),
82226                                 type: type,
82227                                 name: d.display_name,
82228                                 extent: new geoExtent(
82229                                     [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
82230                                     [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
82231                             });
82232                         }
82233                     });
82234
82235                     if (q.match(/^[0-9]+$/)) {
82236                         // if query is just a number, possibly an OSM ID without a prefix
82237                         result.push({
82238                             id: 'n' + q,
82239                             geometry: 'point',
82240                             type: _t('inspector.node'),
82241                             name: q
82242                         });
82243                         result.push({
82244                             id: 'w' + q,
82245                             geometry: 'line',
82246                             type: _t('inspector.way'),
82247                             name: q
82248                         });
82249                         result.push({
82250                             id: 'r' + q,
82251                             geometry: 'relation',
82252                             type: _t('inspector.relation'),
82253                             name: q
82254                         });
82255                     }
82256
82257                     return result;
82258                 }
82259
82260
82261                 function drawList() {
82262                     var value = search.property('value');
82263                     var results = features();
82264
82265                     list.classed('filtered', value.length);
82266
82267                     var resultsIndicator = list.selectAll('.no-results-item')
82268                         .data([0])
82269                         .enter()
82270                         .append('button')
82271                         .property('disabled', true)
82272                         .attr('class', 'no-results-item')
82273                         .call(svgIcon('#iD-icon-alert', 'pre-text'));
82274
82275                     resultsIndicator.append('span')
82276                         .attr('class', 'entity-name');
82277
82278                     list.selectAll('.no-results-item .entity-name')
82279                         .text(_t('geocoder.no_results_worldwide'));
82280
82281                     if (services.geocoder) {
82282                       list.selectAll('.geocode-item')
82283                           .data([0])
82284                           .enter()
82285                           .append('button')
82286                           .attr('class', 'geocode-item')
82287                           .on('click', geocoderSearch)
82288                           .append('div')
82289                           .attr('class', 'label')
82290                           .append('span')
82291                           .attr('class', 'entity-name')
82292                           .text(_t('geocoder.search'));
82293                     }
82294
82295                     list.selectAll('.no-results-item')
82296                         .style('display', (value.length && !results.length) ? 'block' : 'none');
82297
82298                     list.selectAll('.geocode-item')
82299                         .style('display', (value && _geocodeResults === undefined) ? 'block' : 'none');
82300
82301                     list.selectAll('.feature-list-item')
82302                         .data([-1])
82303                         .remove();
82304
82305                     var items = list.selectAll('.feature-list-item')
82306                         .data(results, function(d) { return d.id; });
82307
82308                     var enter = items.enter()
82309                         .insert('button', '.geocode-item')
82310                         .attr('class', 'feature-list-item')
82311                         .on('mouseover', mouseover)
82312                         .on('mouseout', mouseout)
82313                         .on('click', click);
82314
82315                     var label = enter
82316                         .append('div')
82317                         .attr('class', 'label');
82318
82319                     label
82320                         .each(function(d) {
82321                             select(this)
82322                                 .call(svgIcon('#iD-icon-' + d.geometry, 'pre-text'));
82323                         });
82324
82325                     label
82326                         .append('span')
82327                         .attr('class', 'entity-type')
82328                         .text(function(d) { return d.type; });
82329
82330                     label
82331                         .append('span')
82332                         .attr('class', 'entity-name')
82333                         .text(function(d) { return d.name; });
82334
82335                     enter
82336                         .style('opacity', 0)
82337                         .transition()
82338                         .style('opacity', 1);
82339
82340                     items.order();
82341
82342                     items.exit()
82343                         .remove();
82344                 }
82345
82346
82347                 function mouseover(d) {
82348                     if (d.id === -1) return;
82349
82350                     utilHighlightEntities([d.id], true, context);
82351                 }
82352
82353
82354                 function mouseout(d) {
82355                     if (d.id === -1) return;
82356
82357                     utilHighlightEntities([d.id], false, context);
82358                 }
82359
82360
82361                 function click(d) {
82362                     event.preventDefault();
82363
82364                     if (d.location) {
82365                         context.map().centerZoomEase([d.location[1], d.location[0]], 19);
82366
82367                     } else if (d.entity) {
82368                         utilHighlightEntities([d.id], false, context);
82369
82370                         context.enter(modeSelect(context, [d.entity.id]));
82371                         context.map().zoomToEase(d.entity);
82372
82373                     } else {
82374                         // download, zoom to, and select the entity with the given ID
82375                         context.zoomToEntity(d.id);
82376                     }
82377                 }
82378
82379
82380                 function geocoderSearch() {
82381                     services.geocoder.search(search.property('value'), function (err, resp) {
82382                         _geocodeResults = resp || [];
82383                         drawList();
82384                     });
82385                 }
82386             }
82387
82388
82389             return featureList;
82390         }
82391
82392         function uiSectionEntityIssues(context) {
82393
82394             var _entityIDs = [];
82395             var _issues = [];
82396             var _activeIssueID;
82397
82398             var section = uiSection('entity-issues', context)
82399                 .shouldDisplay(function() {
82400                     return _issues.length > 0;
82401                 })
82402                 .title(function() {
82403                     return _t('issues.list_title', { count: _issues.length });
82404                 })
82405                 .disclosureContent(renderDisclosureContent);
82406
82407             context.validator()
82408                 .on('validated.entity_issues', function() {
82409                     // Refresh on validated events
82410                     reloadIssues();
82411                     section.reRender();
82412                 })
82413                 .on('focusedIssue.entity_issues', function(issue) {
82414                      makeActiveIssue(issue.id);
82415                 });
82416
82417             function reloadIssues() {
82418                 _issues = context.validator().getSharedEntityIssues(_entityIDs, { includeDisabledRules: true });
82419             }
82420
82421             function makeActiveIssue(issueID) {
82422                 _activeIssueID = issueID;
82423                 section.selection().selectAll('.issue-container')
82424                     .classed('active', function(d) { return d.id === _activeIssueID; });
82425             }
82426
82427             function renderDisclosureContent(selection) {
82428
82429                 selection.classed('grouped-items-area', true);
82430
82431                 _activeIssueID = _issues.length > 0 ? _issues[0].id : null;
82432
82433                 var containers = selection.selectAll('.issue-container')
82434                     .data(_issues, function(d) { return d.id; });
82435
82436                 // Exit
82437                 containers.exit()
82438                     .remove();
82439
82440                 // Enter
82441                 var containersEnter = containers.enter()
82442                     .append('div')
82443                     .attr('class', 'issue-container');
82444
82445
82446                 var itemsEnter = containersEnter
82447                     .append('div')
82448                     .attr('class', function(d) { return 'issue severity-' + d.severity; })
82449                     .on('mouseover.highlight', function(d) {
82450                         // don't hover-highlight the selected entity
82451                         var ids = d.entityIds
82452                             .filter(function(e) { return _entityIDs.indexOf(e) === -1; });
82453
82454                         utilHighlightEntities(ids, true, context);
82455                     })
82456                     .on('mouseout.highlight', function(d) {
82457                         var ids = d.entityIds
82458                             .filter(function(e) { return _entityIDs.indexOf(e) === -1; });
82459
82460                         utilHighlightEntities(ids, false, context);
82461                     });
82462
82463                 var labelsEnter = itemsEnter
82464                     .append('div')
82465                     .attr('class', 'issue-label')
82466                     .on('click', function(d) {
82467
82468                         makeActiveIssue(d.id); // expand only the clicked item
82469
82470                         var extent = d.extent(context.graph());
82471                         if (extent) {
82472                             var setZoom = Math.max(context.map().zoom(), 19);
82473                             context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
82474                         }
82475                     });
82476
82477                 var textEnter = labelsEnter
82478                     .append('span')
82479                     .attr('class', 'issue-text');
82480
82481                 textEnter
82482                     .append('span')
82483                     .attr('class', 'issue-icon')
82484                     .each(function(d) {
82485                         var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
82486                         select(this)
82487                             .call(svgIcon(iconName));
82488                     });
82489
82490                 textEnter
82491                     .append('span')
82492                     .attr('class', 'issue-message');
82493
82494
82495                 var infoButton = labelsEnter
82496                     .append('button')
82497                     .attr('class', 'issue-info-button')
82498                     .attr('title', _t('icons.information'))
82499                     .attr('tabindex', -1)
82500                     .call(svgIcon('#iD-icon-inspect'));
82501
82502                 infoButton
82503                     .on('click', function () {
82504                         event.stopPropagation();
82505                         event.preventDefault();
82506                         this.blur();    // avoid keeping focus on the button - #4641
82507
82508                         var container = select(this.parentNode.parentNode.parentNode);
82509                         var info = container.selectAll('.issue-info');
82510                         var isExpanded = info.classed('expanded');
82511
82512                         if (isExpanded) {
82513                             info
82514                                 .transition()
82515                                 .duration(200)
82516                                 .style('max-height', '0px')
82517                                 .style('opacity', '0')
82518                                 .on('end', function () {
82519                                     info.classed('expanded', false);
82520                                 });
82521                         } else {
82522                             info
82523                                 .classed('expanded', true)
82524                                 .transition()
82525                                 .duration(200)
82526                                 .style('max-height', '200px')
82527                                 .style('opacity', '1')
82528                                 .on('end', function () {
82529                                     info.style('max-height', null);
82530                                 });
82531                         }
82532                     });
82533
82534                 itemsEnter
82535                     .append('ul')
82536                     .attr('class', 'issue-fix-list');
82537
82538                 containersEnter
82539                     .append('div')
82540                     .attr('class', 'issue-info')
82541                     .style('max-height', '0')
82542                     .style('opacity', '0')
82543                     .each(function(d) {
82544                         if (typeof d.reference === 'function') {
82545                             select(this)
82546                                 .call(d.reference);
82547                         } else {
82548                             select(this)
82549                                 .text(_t('inspector.no_documentation_key'));
82550                         }
82551                     });
82552
82553
82554                 // Update
82555                 containers = containers
82556                     .merge(containersEnter)
82557                     .classed('active', function(d) { return d.id === _activeIssueID; });
82558
82559                 containers.selectAll('.issue-message')
82560                     .text(function(d) {
82561                         return d.message(context);
82562                     });
82563
82564                 // fixes
82565                 var fixLists = containers.selectAll('.issue-fix-list');
82566
82567                 var fixes = fixLists.selectAll('.issue-fix-item')
82568                     .data(function(d) { return d.fixes ? d.fixes(context) : []; }, function(fix) { return fix.id; });
82569
82570                 fixes.exit()
82571                     .remove();
82572
82573                 var fixesEnter = fixes.enter()
82574                     .append('li')
82575                     .attr('class', 'issue-fix-item')
82576                     .on('click', function(d) {
82577                         // not all fixes are actionable
82578                         if (!select(this).classed('actionable') || !d.onClick) return;
82579
82580                         // Don't run another fix for this issue within a second of running one
82581                         // (Necessary for "Select a feature type" fix. Most fixes should only ever run once)
82582                         if (d.issue.dateLastRanFix && new Date() - d.issue.dateLastRanFix < 1000) return;
82583                         d.issue.dateLastRanFix = new Date();
82584
82585                         // remove hover-highlighting
82586                         utilHighlightEntities(d.issue.entityIds.concat(d.entityIds), false, context);
82587
82588                         new Promise(function(resolve, reject) {
82589                             d.onClick(context, resolve, reject);
82590                             if (d.onClick.length <= 1) {
82591                                 // if the fix doesn't take any completion parameters then consider it resolved
82592                                 resolve();
82593                             }
82594                         })
82595                         .then(function() {
82596                             // revalidate whenever the fix has finished running successfully
82597                             context.validator().validate();
82598                         });
82599                     })
82600                     .on('mouseover.highlight', function(d) {
82601                         utilHighlightEntities(d.entityIds, true, context);
82602                     })
82603                     .on('mouseout.highlight', function(d) {
82604                         utilHighlightEntities(d.entityIds, false, context);
82605                     });
82606
82607                 fixesEnter
82608                     .append('span')
82609                     .attr('class', 'fix-icon')
82610                     .each(function(d) {
82611                         var iconName = d.icon || 'iD-icon-wrench';
82612                         if (iconName.startsWith('maki')) {
82613                             iconName += '-15';
82614                         }
82615                         select(this).call(svgIcon('#' + iconName));
82616                     });
82617
82618                 fixesEnter
82619                     .append('span')
82620                     .attr('class', 'fix-message')
82621                     .text(function(d) { return d.title; });
82622
82623                 fixesEnter.merge(fixes)
82624                     .classed('actionable', function(d) {
82625                         return d.onClick;
82626                     })
82627                     .attr('title', function(d) {
82628                         if (d.disabledReason) {
82629                             return d.disabledReason;
82630                         }
82631                         return null;
82632                     });
82633             }
82634
82635             section.entityIDs = function(val) {
82636                 if (!arguments.length) return _entityIDs;
82637                 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
82638                     _entityIDs = val;
82639                     _activeIssueID = null;
82640                     reloadIssues();
82641                 }
82642                 return section;
82643             };
82644
82645             return section;
82646         }
82647
82648         function uiPresetIcon() {
82649           let _preset;
82650           let _geometry;
82651           let _sizeClass = 'medium';
82652
82653
82654           function isSmall() {
82655             return _sizeClass === 'small';
82656           }
82657
82658
82659           function presetIcon(selection) {
82660             selection.each(render);
82661           }
82662
82663
82664           function getIcon(p, geom) {
82665             if (isSmall() && p.isFallback && p.isFallback())
82666               return 'iD-icon-' + p.id;
82667             else if (p.icon)
82668               return p.icon;
82669             else if (geom === 'line')
82670               return 'iD-other-line';
82671             else if (geom === 'vertex')
82672               return p.isFallback() ? '' : 'temaki-vertex';
82673             else if (isSmall() && geom === 'point')
82674               return '';
82675             else
82676               return 'maki-marker-stroked';
82677           }
82678
82679
82680           function renderPointBorder(enter) {
82681             const w = 40;
82682             const h = 40;
82683
82684             enter
82685               .append('svg')
82686               .attr('class', 'preset-icon-fill preset-icon-point-border')
82687               .attr('width', w)
82688               .attr('height', h)
82689               .attr('viewBox', `0 0 ${w} ${h}`)
82690               .append('path')
82691               .attr('transform', 'translate(11.5, 8)')
82692               .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');
82693           }
82694
82695
82696           function renderCircleFill(fillEnter) {
82697             const w = 60;
82698             const h = 60;
82699             const d = 40;
82700
82701             fillEnter
82702               .append('svg')
82703               .attr('class', 'preset-icon-fill preset-icon-fill-vertex')
82704               .attr('width', w)
82705               .attr('height', h)
82706               .attr('viewBox', `0 0 ${w} ${h}`)
82707               .append('circle')
82708               .attr('cx', w / 2)
82709               .attr('cy', h / 2)
82710               .attr('r', d / 2);
82711           }
82712
82713
82714           function renderSquareFill(fillEnter) {
82715             const d = isSmall() ? 40 : 60;
82716             const w = d;
82717             const h = d;
82718             const l = d * 2/3;
82719             const c1 = (w-l) / 2;
82720             const c2 = c1 + l;
82721
82722             fillEnter = fillEnter
82723               .append('svg')
82724               .attr('class', 'preset-icon-fill preset-icon-fill-area')
82725               .attr('width', w)
82726               .attr('height', h)
82727               .attr('viewBox', `0 0 ${w} ${h}`);
82728
82729             ['fill', 'stroke'].forEach(klass => {
82730               fillEnter
82731                 .append('path')
82732                 .attr('d', `M${c1} ${c1} L${c1} ${c2} L${c2} ${c2} L${c2} ${c1} Z`)
82733                 .attr('class', `line area ${klass}`);
82734             });
82735
82736             const rVertex = 2.5;
82737             [[c1, c1], [c1, c2], [c2, c2], [c2, c1]].forEach(point => {
82738               fillEnter
82739                 .append('circle')
82740                 .attr('class', 'vertex')
82741                 .attr('cx', point[0])
82742                 .attr('cy', point[1])
82743                 .attr('r', rVertex);
82744             });
82745
82746             if (!isSmall()) {
82747               const rMidpoint = 1.25;
82748               [[c1, w/2], [c2, w/2], [h/2, c1], [h/2, c2]].forEach(point => {
82749                 fillEnter
82750                   .append('circle')
82751                   .attr('class', 'midpoint')
82752                   .attr('cx', point[0])
82753                   .attr('cy', point[1])
82754                   .attr('r', rMidpoint);
82755               });
82756             }
82757           }
82758
82759
82760           function renderLine(lineEnter) {
82761             const d = isSmall() ? 40 : 60;
82762             // draw the line parametrically
82763             const w = d;
82764             const h = d;
82765             const y = Math.round(d * 0.72);
82766             const l = Math.round(d * 0.6);
82767             const r = 2.5;
82768             const x1 = (w - l) / 2;
82769             const x2 = x1 + l;
82770
82771             lineEnter = lineEnter
82772               .append('svg')
82773               .attr('class', 'preset-icon-line')
82774               .attr('width', w)
82775               .attr('height', h)
82776               .attr('viewBox', `0 0 ${w} ${h}`);
82777
82778             ['casing', 'stroke'].forEach(klass => {
82779               lineEnter
82780                 .append('path')
82781                 .attr('d', `M${x1} ${y} L${x2} ${y}`)
82782                 .attr('class', `line ${klass}`);
82783             });
82784
82785             [[x1-1, y], [x2+1, y]].forEach(point => {
82786               lineEnter
82787                 .append('circle')
82788                 .attr('class', 'vertex')
82789                 .attr('cx', point[0])
82790                 .attr('cy', point[1])
82791                 .attr('r', r);
82792             });
82793           }
82794
82795
82796           function renderRoute(routeEnter) {
82797             const d = isSmall() ? 40 : 60;
82798             // draw the route parametrically
82799             const w = d;
82800             const h = d;
82801             const y1 = Math.round(d * 0.80);
82802             const y2 = Math.round(d * 0.68);
82803             const l = Math.round(d * 0.6);
82804             const r = 2;
82805             const x1 = (w - l) / 2;
82806             const x2 = x1 + l / 3;
82807             const x3 = x2 + l / 3;
82808             const x4 = x3 + l / 3;
82809
82810             routeEnter = routeEnter
82811               .append('svg')
82812               .attr('class', 'preset-icon-route')
82813               .attr('width', w)
82814               .attr('height', h)
82815               .attr('viewBox', `0 0 ${w} ${h}`);
82816
82817             ['casing', 'stroke'].forEach(klass => {
82818               routeEnter
82819                 .append('path')
82820                 .attr('d', `M${x1} ${y1} L${x2} ${y2}`)
82821                 .attr('class', `segment0 line ${klass}`);
82822               routeEnter
82823                 .append('path')
82824                 .attr('d', `M${x2} ${y2} L${x3} ${y1}`)
82825                 .attr('class', `segment1 line ${klass}`);
82826               routeEnter
82827                 .append('path')
82828                 .attr('d', `M${x3} ${y1} L${x4} ${y2}`)
82829                 .attr('class', `segment2 line ${klass}`);
82830             });
82831
82832             [[x1, y1], [x2, y2], [x3, y1], [x4, y2]].forEach(point => {
82833               routeEnter
82834                 .append('circle')
82835                 .attr('class', 'vertex')
82836                 .attr('cx', point[0])
82837                 .attr('cy', point[1])
82838                 .attr('r', r);
82839             });
82840           }
82841
82842
82843           // Route icons are drawn with a zigzag annotation underneath:
82844           //     o   o
82845           //    / \ /
82846           //   o   o
82847           // This dataset defines the styles that are used to draw the zigzag segments.
82848           const routeSegments = {
82849             bicycle: ['highway/cycleway', 'highway/cycleway', 'highway/cycleway'],
82850             bus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
82851             trolleybus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
82852             detour: ['highway/tertiary', 'highway/residential', 'highway/unclassified'],
82853             ferry: ['route/ferry', 'route/ferry', 'route/ferry'],
82854             foot: ['highway/footway', 'highway/footway', 'highway/footway'],
82855             hiking: ['highway/path', 'highway/path', 'highway/path'],
82856             horse: ['highway/bridleway', 'highway/bridleway', 'highway/bridleway'],
82857             light_rail: ['railway/light_rail', 'railway/light_rail', 'railway/light_rail'],
82858             monorail: ['railway/monorail', 'railway/monorail', 'railway/monorail'],
82859             pipeline: ['man_made/pipeline', 'man_made/pipeline', 'man_made/pipeline'],
82860             piste: ['piste/downhill', 'piste/hike', 'piste/nordic'],
82861             power: ['power/line', 'power/line', 'power/line'],
82862             road: ['highway/secondary', 'highway/primary', 'highway/trunk'],
82863             subway: ['railway/subway', 'railway/subway', 'railway/subway'],
82864             train: ['railway/rail', 'railway/rail', 'railway/rail'],
82865             tram: ['railway/tram', 'railway/tram', 'railway/tram'],
82866             waterway: ['waterway/stream', 'waterway/stream', 'waterway/stream']
82867           };
82868
82869
82870           function render() {
82871             let p = _preset.apply(this, arguments);
82872             let geom = _geometry ? _geometry.apply(this, arguments) : null;
82873             if (geom === 'relation' && p.tags && ((p.tags.type === 'route' && p.tags.route && routeSegments[p.tags.route]) || p.tags.type === 'waterway')) {
82874               geom = 'route';
82875             }
82876
82877             const showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
82878             const isFallback = isSmall() && p.isFallback && p.isFallback();
82879             const imageURL = (showThirdPartyIcons === 'true') && p.imageURL;
82880             const picon = getIcon(p, geom);
82881             const isMaki = picon && /^maki-/.test(picon);
82882             const isTemaki = picon && /^temaki-/.test(picon);
82883             const isFa = picon && /^fa[srb]-/.test(picon);
82884             const isTnp = picon && /^tnp-/.test(picon);
82885             const isiDIcon = picon && !(isMaki || isTemaki || isFa || isTnp);
82886             const isCategory = !p.setTags;
82887             const drawPoint = picon && geom === 'point' && isSmall() && !isFallback;
82888             const drawVertex = picon !== null && geom === 'vertex' && (!isSmall() || !isFallback);
82889             const drawLine = picon && geom === 'line' && !isFallback && !isCategory;
82890             const drawArea = picon && geom === 'area' && !isFallback;
82891             const drawRoute = picon && geom === 'route';
82892             const isFramed = (drawVertex || drawArea || drawLine || drawRoute);
82893
82894             let tags = !isCategory ? p.setTags({}, geom) : {};
82895             for (let k in tags) {
82896               if (tags[k] === '*') {
82897                 tags[k] = 'yes';
82898               }
82899             }
82900
82901             let tagClasses = svgTagClasses().getClassesString(tags, '');
82902             let selection = select(this);
82903
82904             let container = selection.selectAll('.preset-icon-container')
82905               .data([0]);
82906
82907             container = container.enter()
82908               .append('div')
82909               .attr('class', `preset-icon-container ${_sizeClass}`)
82910               .merge(container);
82911
82912             container
82913               .classed('showing-img', !!imageURL)
82914               .classed('fallback', isFallback);
82915
82916
82917             let pointBorder = container.selectAll('.preset-icon-point-border')
82918               .data(drawPoint ? [0] : []);
82919
82920             pointBorder.exit()
82921               .remove();
82922
82923             let pointBorderEnter = pointBorder.enter();
82924             renderPointBorder(pointBorderEnter);
82925             pointBorder = pointBorderEnter.merge(pointBorder);
82926
82927
82928             let vertexFill = container.selectAll('.preset-icon-fill-vertex')
82929               .data(drawVertex ? [0] : []);
82930
82931             vertexFill.exit()
82932               .remove();
82933
82934             let vertexFillEnter = vertexFill.enter();
82935             renderCircleFill(vertexFillEnter);
82936             vertexFill = vertexFillEnter.merge(vertexFill);
82937
82938
82939             let fill = container.selectAll('.preset-icon-fill-area')
82940               .data(drawArea ? [0] : []);
82941
82942             fill.exit()
82943               .remove();
82944
82945             let fillEnter = fill.enter();
82946             renderSquareFill(fillEnter);
82947             fill = fillEnter.merge(fill);
82948
82949             fill.selectAll('path.stroke')
82950               .attr('class', `area stroke ${tagClasses}`);
82951             fill.selectAll('path.fill')
82952               .attr('class', `area fill ${tagClasses}`);
82953
82954
82955             let line = container.selectAll('.preset-icon-line')
82956               .data(drawLine ? [0] : []);
82957
82958             line.exit()
82959               .remove();
82960
82961             let lineEnter = line.enter();
82962             renderLine(lineEnter);
82963             line = lineEnter.merge(line);
82964
82965             line.selectAll('path.stroke')
82966               .attr('class', `line stroke ${tagClasses}`);
82967             line.selectAll('path.casing')
82968               .attr('class', `line casing ${tagClasses}`);
82969
82970
82971             let route = container.selectAll('.preset-icon-route')
82972               .data(drawRoute ? [0] : []);
82973
82974             route.exit()
82975               .remove();
82976
82977             let routeEnter = route.enter();
82978             renderRoute(routeEnter);
82979             route = routeEnter.merge(route);
82980
82981             if (drawRoute) {
82982               let routeType = p.tags.type === 'waterway' ? 'waterway' : p.tags.route;
82983               const segmentPresetIDs = routeSegments[routeType];
82984               for (let i in segmentPresetIDs) {
82985                 const segmentPreset = _mainPresetIndex.item(segmentPresetIDs[i]);
82986                 const segmentTagClasses = svgTagClasses().getClassesString(segmentPreset.tags, '');
82987                 route.selectAll(`path.stroke.segment${i}`)
82988                   .attr('class', `segment${i} line stroke ${segmentTagClasses}`);
82989                 route.selectAll(`path.casing.segment${i}`)
82990                   .attr('class', `segment${i} line casing ${segmentTagClasses}`);
82991               }
82992             }
82993
82994
82995             let icon = container.selectAll('.preset-icon')
82996               .data(picon ? [0] : []);
82997
82998             icon.exit()
82999               .remove();
83000
83001             icon = icon.enter()
83002               .append('div')
83003               .attr('class', 'preset-icon')
83004               .call(svgIcon(''))
83005               .merge(icon);
83006
83007             icon
83008               .attr('class', 'preset-icon ' + (geom ? geom + '-geom' : ''))
83009               .classed('framed', isFramed)
83010               .classed('preset-icon-iD', isiDIcon);
83011
83012             icon.selectAll('svg')
83013               .attr('class', 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line'  ? '' : tagClasses));
83014
83015             icon.selectAll('use')
83016               .attr('href', '#' + picon + (isMaki ? (isSmall() && geom === 'point' ? '-11' : '-15') : ''));
83017
83018             let imageIcon = container.selectAll('img.image-icon')
83019               .data(imageURL ? [0] : []);
83020
83021             imageIcon.exit()
83022               .remove();
83023
83024             imageIcon = imageIcon.enter()
83025               .append('img')
83026               .attr('class', 'image-icon')
83027               .on('load', () => container.classed('showing-img', true) )
83028               .on('error', () => container.classed('showing-img', false) )
83029               .merge(imageIcon);
83030
83031             imageIcon
83032               .attr('src', imageURL);
83033           }
83034
83035
83036           presetIcon.preset = function(val) {
83037             if (!arguments.length) return _preset;
83038             _preset = utilFunctor(val);
83039             return presetIcon;
83040           };
83041
83042
83043           presetIcon.geometry = function(val) {
83044             if (!arguments.length) return _geometry;
83045             _geometry = utilFunctor(val);
83046             return presetIcon;
83047           };
83048
83049
83050           presetIcon.sizeClass = function(val) {
83051             if (!arguments.length) return _sizeClass;
83052             _sizeClass = val;
83053             return presetIcon;
83054           };
83055
83056           return presetIcon;
83057         }
83058
83059         function uiSectionFeatureType(context) {
83060
83061             var dispatch$1 = dispatch('choose');
83062
83063             var _entityIDs = [];
83064             var _presets = [];
83065
83066             var _tagReference;
83067
83068             var section = uiSection('feature-type', context)
83069                 .title(_t('inspector.feature_type'))
83070                 .disclosureContent(renderDisclosureContent);
83071
83072             function renderDisclosureContent(selection) {
83073
83074                 selection.classed('preset-list-item', true);
83075                 selection.classed('mixed-types', _presets.length > 1);
83076
83077                 var presetButtonWrap = selection
83078                     .selectAll('.preset-list-button-wrap')
83079                     .data([0])
83080                     .enter()
83081                     .append('div')
83082                     .attr('class', 'preset-list-button-wrap');
83083
83084                 var presetButton = presetButtonWrap
83085                     .append('button')
83086                     .attr('class', 'preset-list-button preset-reset')
83087                     .call(uiTooltip()
83088                         .title(_t('inspector.back_tooltip'))
83089                         .placement('bottom')
83090                     );
83091
83092                 presetButton.append('div')
83093                     .attr('class', 'preset-icon-container');
83094
83095                 presetButton
83096                     .append('div')
83097                     .attr('class', 'label')
83098                     .append('div')
83099                     .attr('class', 'label-inner');
83100
83101                 presetButtonWrap.append('div')
83102                     .attr('class', 'accessory-buttons');
83103
83104                 var tagReferenceBodyWrap = selection
83105                     .selectAll('.tag-reference-body-wrap')
83106                     .data([0]);
83107
83108                 tagReferenceBodyWrap = tagReferenceBodyWrap
83109                     .enter()
83110                     .append('div')
83111                     .attr('class', 'tag-reference-body-wrap')
83112                     .merge(tagReferenceBodyWrap);
83113
83114                 // update header
83115                 if (_tagReference) {
83116                     selection.selectAll('.preset-list-button-wrap .accessory-buttons')
83117                         .style('display', _presets.length === 1 ? null : 'none')
83118                         .call(_tagReference.button);
83119
83120                     tagReferenceBodyWrap
83121                         .style('display', _presets.length === 1 ? null : 'none')
83122                         .call(_tagReference.body);
83123                 }
83124
83125                 selection.selectAll('.preset-reset')
83126                     .on('click', function() {
83127                          dispatch$1.call('choose', this, _presets);
83128                     })
83129                     .on('pointerdown pointerup mousedown mouseup', function() {
83130                         event.preventDefault();
83131                         event.stopPropagation();
83132                     });
83133
83134                 var geometries = entityGeometries();
83135                 selection.select('.preset-list-item button')
83136                     .call(uiPresetIcon()
83137                         .geometry(_presets.length === 1 ? (geometries.length === 1 && geometries[0]) : null)
83138                         .preset(_presets.length === 1 ? _presets[0] : _mainPresetIndex.item('point'))
83139                     );
83140
83141                 // NOTE: split on en-dash, not a hypen (to avoid conflict with hyphenated names)
83142                 var names = _presets.length === 1 ? _presets[0].name().split(' – ') : [_t('inspector.multiple_types')];
83143
83144                 var label = selection.select('.label-inner');
83145                 var nameparts = label.selectAll('.namepart')
83146                     .data(names, function(d) { return d; });
83147
83148                 nameparts.exit()
83149                     .remove();
83150
83151                 nameparts
83152                     .enter()
83153                     .append('div')
83154                     .attr('class', 'namepart')
83155                     .text(function(d) { return d; });
83156             }
83157
83158             section.entityIDs = function(val) {
83159                 if (!arguments.length) return _entityIDs;
83160                 _entityIDs = val;
83161                 return section;
83162             };
83163
83164             section.presets = function(val) {
83165                 if (!arguments.length) return _presets;
83166
83167                 // don't reload the same preset
83168                 if (!utilArrayIdentical(val, _presets)) {
83169                     _presets = val;
83170
83171                     var geometries = entityGeometries();
83172                     if (_presets.length === 1 && geometries.length) {
83173                         _tagReference = uiTagReference(_presets[0].reference(geometries[0]))
83174                             .showing(false);
83175                     }
83176                 }
83177
83178                 return section;
83179             };
83180
83181             function entityGeometries() {
83182
83183                 var counts = {};
83184
83185                 for (var i in _entityIDs) {
83186                     var geometry = context.graph().geometry(_entityIDs[i]);
83187                     if (!counts[geometry]) counts[geometry] = 0;
83188                     counts[geometry] += 1;
83189                 }
83190
83191                 return Object.keys(counts).sort(function(geom1, geom2) {
83192                     return counts[geom2] - counts[geom1];
83193                 });
83194             }
83195
83196             return utilRebind(section, dispatch$1, 'on');
83197         }
83198
83199         // This currently only works with the 'restrictions' field
83200         // It borrows some code from uiHelp
83201
83202         function uiFieldHelp(context, fieldName) {
83203             var fieldHelp = {};
83204             var _inspector = select(null);
83205             var _wrap = select(null);
83206             var _body = select(null);
83207
83208             var fieldHelpKeys = {
83209                 restrictions: [
83210                     ['about',[
83211                         'about',
83212                         'from_via_to',
83213                         'maxdist',
83214                         'maxvia'
83215                     ]],
83216                     ['inspecting',[
83217                         'about',
83218                         'from_shadow',
83219                         'allow_shadow',
83220                         'restrict_shadow',
83221                         'only_shadow',
83222                         'restricted',
83223                         'only'
83224                     ]],
83225                     ['modifying',[
83226                         'about',
83227                         'indicators',
83228                         'allow_turn',
83229                         'restrict_turn',
83230                         'only_turn'
83231                     ]],
83232                     ['tips',[
83233                         'simple',
83234                         'simple_example',
83235                         'indirect',
83236                         'indirect_example',
83237                         'indirect_noedit'
83238                     ]]
83239                 ]
83240             };
83241
83242             var fieldHelpHeadings = {};
83243
83244             var replacements = {
83245                 distField: _t('restriction.controls.distance'),
83246                 viaField: _t('restriction.controls.via'),
83247                 fromShadow: icon('#iD-turn-shadow', 'pre-text shadow from'),
83248                 allowShadow: icon('#iD-turn-shadow', 'pre-text shadow allow'),
83249                 restrictShadow: icon('#iD-turn-shadow', 'pre-text shadow restrict'),
83250                 onlyShadow: icon('#iD-turn-shadow', 'pre-text shadow only'),
83251                 allowTurn: icon('#iD-turn-yes', 'pre-text turn'),
83252                 restrictTurn: icon('#iD-turn-no', 'pre-text turn'),
83253                 onlyTurn: icon('#iD-turn-only', 'pre-text turn')
83254             };
83255
83256
83257             // For each section, squash all the texts into a single markdown document
83258             var docs = fieldHelpKeys[fieldName].map(function(key) {
83259                 var helpkey = 'help.field.' + fieldName + '.' + key[0];
83260                 var text = key[1].reduce(function(all, part) {
83261                     var subkey = helpkey + '.' + part;
83262                     var depth = fieldHelpHeadings[subkey];                     // is this subkey a heading?
83263                     var hhh = depth ? Array(depth + 1).join('#') + ' ' : '';   // if so, prepend with some ##'s
83264                     return all + hhh + _t(subkey, replacements) + '\n\n';
83265                 }, '');
83266
83267                 return {
83268                     key: helpkey,
83269                     title: _t(helpkey + '.title'),
83270                     html: marked_1(text.trim())
83271                 };
83272             });
83273
83274
83275             function show() {
83276                 updatePosition();
83277
83278                 _body
83279                     .classed('hide', false)
83280                     .style('opacity', '0')
83281                     .transition()
83282                     .duration(200)
83283                     .style('opacity', '1');
83284             }
83285
83286
83287             function hide() {
83288                 _body
83289                     .classed('hide', true)
83290                     .transition()
83291                     .duration(200)
83292                     .style('opacity', '0')
83293                     .on('end', function () {
83294                         _body.classed('hide', true);
83295                     });
83296             }
83297
83298
83299             function clickHelp(index) {
83300                 var d = docs[index];
83301                 var tkeys = fieldHelpKeys[fieldName][index][1];
83302
83303                 _body.selectAll('.field-help-nav-item')
83304                     .classed('active', function(d, i) { return i === index; });
83305
83306                 var content = _body.selectAll('.field-help-content')
83307                     .html(d.html);
83308
83309                 // class the paragraphs so we can find and style them
83310                 content.selectAll('p')
83311                     .attr('class', function(d, i) { return tkeys[i]; });
83312
83313                 // insert special content for certain help sections
83314                 if (d.key === 'help.field.restrictions.inspecting') {
83315                     content
83316                         .insert('img', 'p.from_shadow')
83317                         .attr('class', 'field-help-image cf')
83318                         .attr('src', context.imagePath('tr_inspect.gif'));
83319
83320                 } else if (d.key === 'help.field.restrictions.modifying') {
83321                     content
83322                         .insert('img', 'p.allow_turn')
83323                         .attr('class', 'field-help-image cf')
83324                         .attr('src', context.imagePath('tr_modify.gif'));
83325                 }
83326             }
83327
83328
83329             fieldHelp.button = function(selection) {
83330                 if (_body.empty()) return;
83331
83332                 var button = selection.selectAll('.field-help-button')
83333                     .data([0]);
83334
83335                 // enter/update
83336                 button.enter()
83337                     .append('button')
83338                     .attr('class', 'field-help-button')
83339                     .attr('tabindex', -1)
83340                     .call(svgIcon('#iD-icon-help'))
83341                     .merge(button)
83342                     .on('click', function () {
83343                         event.stopPropagation();
83344                         event.preventDefault();
83345                         if (_body.classed('hide')) {
83346                             show();
83347                         } else {
83348                             hide();
83349                         }
83350                     });
83351             };
83352
83353
83354             function updatePosition() {
83355                 var wrap = _wrap.node();
83356                 var inspector = _inspector.node();
83357                 var wRect = wrap.getBoundingClientRect();
83358                 var iRect = inspector.getBoundingClientRect();
83359
83360                 _body
83361                     .style('top', wRect.top + inspector.scrollTop - iRect.top + 'px');
83362             }
83363
83364
83365             fieldHelp.body = function(selection) {
83366                 // This control expects the field to have a form-field-input-wrap div
83367                 _wrap = selection.selectAll('.form-field-input-wrap');
83368                 if (_wrap.empty()) return;
83369
83370                 // absolute position relative to the inspector, so it "floats" above the fields
83371                 _inspector = context.container().select('.sidebar .entity-editor-pane .inspector-body');
83372                 if (_inspector.empty()) return;
83373
83374                 _body = _inspector.selectAll('.field-help-body')
83375                     .data([0]);
83376
83377                 var enter = _body.enter()
83378                     .append('div')
83379                     .attr('class', 'field-help-body hide');   // initially hidden
83380
83381                 var titleEnter = enter
83382                     .append('div')
83383                     .attr('class', 'field-help-title cf');
83384
83385                 titleEnter
83386                     .append('h2')
83387                     .attr('class', ((_mainLocalizer.textDirection() === 'rtl') ? 'fr' : 'fl'))
83388                     .text(_t('help.field.' + fieldName + '.title'));
83389
83390                 titleEnter
83391                     .append('button')
83392                     .attr('class', 'fr close')
83393                     .on('click', function() {
83394                         event.stopPropagation();
83395                         event.preventDefault();
83396                         hide();
83397                     })
83398                     .call(svgIcon('#iD-icon-close'));
83399
83400                 var navEnter = enter
83401                     .append('div')
83402                     .attr('class', 'field-help-nav cf');
83403
83404                 var titles = docs.map(function(d) { return d.title; });
83405                 navEnter.selectAll('.field-help-nav-item')
83406                     .data(titles)
83407                     .enter()
83408                     .append('div')
83409                     .attr('class', 'field-help-nav-item')
83410                     .text(function(d) { return d; })
83411                     .on('click', function(d, i) {
83412                         event.stopPropagation();
83413                         event.preventDefault();
83414                         clickHelp(i);
83415                     });
83416
83417                 enter
83418                     .append('div')
83419                     .attr('class', 'field-help-content');
83420
83421                 _body = _body
83422                     .merge(enter);
83423
83424                 clickHelp(0);
83425             };
83426
83427
83428             return fieldHelp;
83429         }
83430
83431         function uiFieldCheck(field, context) {
83432             var dispatch$1 = dispatch('change');
83433             var options = field.strings && field.strings.options;
83434             var values = [];
83435             var texts = [];
83436
83437             var _tags;
83438
83439             var input = select(null);
83440             var text = select(null);
83441             var label = select(null);
83442             var reverser = select(null);
83443
83444             var _impliedYes;
83445             var _entityIDs = [];
83446             var _value;
83447
83448
83449             if (options) {
83450                 for (var k in options) {
83451                     values.push(k === 'undefined' ? undefined : k);
83452                     texts.push(field.t('options.' + k, { 'default': options[k] }));
83453                 }
83454             } else {
83455                 values = [undefined, 'yes'];
83456                 texts = [_t('inspector.unknown'), _t('inspector.check.yes')];
83457                 if (field.type !== 'defaultCheck') {
83458                     values.push('no');
83459                     texts.push(_t('inspector.check.no'));
83460                 }
83461             }
83462
83463
83464             // Checks tags to see whether an undefined value is "Assumed to be Yes"
83465             function checkImpliedYes() {
83466                 _impliedYes = (field.id === 'oneway_yes');
83467
83468                 // hack: pretend `oneway` field is a `oneway_yes` field
83469                 // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
83470                 if (field.id === 'oneway') {
83471                     var entity = context.entity(_entityIDs[0]);
83472                     for (var key in entity.tags) {
83473                         if (key in osmOneWayTags && (entity.tags[key] in osmOneWayTags[key])) {
83474                             _impliedYes = true;
83475                             texts[0] = _t('presets.fields.oneway_yes.options.undefined');
83476                             break;
83477                         }
83478                     }
83479                 }
83480             }
83481
83482
83483             function reverserHidden() {
83484                 if (!context.container().select('div.inspector-hover').empty()) return true;
83485                 return !(_value === 'yes' || (_impliedYes && !_value));
83486             }
83487
83488
83489             function reverserSetText(selection) {
83490                 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
83491                 if (reverserHidden() || !entity) return selection;
83492
83493                 var first = entity.first();
83494                 var last = entity.isClosed() ? entity.nodes[entity.nodes.length - 2] : entity.last();
83495                 var pseudoDirection = first < last;
83496                 var icon = pseudoDirection ? '#iD-icon-forward' : '#iD-icon-backward';
83497
83498                 selection.selectAll('.reverser-span')
83499                     .text(_t('inspector.check.reverser'))
83500                     .call(svgIcon(icon, 'inline'));
83501
83502                 return selection;
83503             }
83504
83505
83506             var check = function(selection) {
83507                 checkImpliedYes();
83508
83509                 label = selection.selectAll('.form-field-input-wrap')
83510                     .data([0]);
83511
83512                 var enter = label.enter()
83513                     .append('label')
83514                     .attr('class', 'form-field-input-wrap form-field-input-check');
83515
83516                 enter
83517                     .append('input')
83518                     .property('indeterminate', field.type !== 'defaultCheck')
83519                     .attr('type', 'checkbox')
83520                     .attr('id', field.domId);
83521
83522                 enter
83523                     .append('span')
83524                     .text(texts[0])
83525                     .attr('class', 'value');
83526
83527                 if (field.type === 'onewayCheck') {
83528                     enter
83529                         .append('a')
83530                         .attr('class', 'reverser button' + (reverserHidden() ? ' hide' : ''))
83531                         .attr('href', '#')
83532                         .append('span')
83533                         .attr('class', 'reverser-span');
83534                 }
83535
83536                 label = label.merge(enter);
83537                 input = label.selectAll('input');
83538                 text = label.selectAll('span.value');
83539
83540                 input
83541                     .on('click', function() {
83542                         event.stopPropagation();
83543                         var t = {};
83544
83545                         if (Array.isArray(_tags[field.key])) {
83546                             if (values.indexOf('yes') !== -1) {
83547                                 t[field.key] = 'yes';
83548                             } else {
83549                                 t[field.key] = values[0];
83550                             }
83551                         } else {
83552                             t[field.key] = values[(values.indexOf(_value) + 1) % values.length];
83553                         }
83554
83555                         // Don't cycle through `alternating` or `reversible` states - #4970
83556                         // (They are supported as translated strings, but should not toggle with clicks)
83557                         if (t[field.key] === 'reversible' || t[field.key] === 'alternating') {
83558                             t[field.key] = values[0];
83559                         }
83560
83561                         dispatch$1.call('change', this, t);
83562                     });
83563
83564                 if (field.type === 'onewayCheck') {
83565                     reverser = label.selectAll('.reverser');
83566
83567                     reverser
83568                         .call(reverserSetText)
83569                         .on('click', function() {
83570                             event.preventDefault();
83571                             event.stopPropagation();
83572                             context.perform(
83573                                 function(graph) {
83574                                     for (var i in _entityIDs) {
83575                                         graph = actionReverse(_entityIDs[i])(graph);
83576                                     }
83577                                     return graph;
83578                                 },
83579                                 _t('operations.reverse.annotation')
83580                             );
83581
83582                             // must manually revalidate since no 'change' event was called
83583                             context.validator().validate();
83584
83585                             select(this)
83586                                 .call(reverserSetText);
83587                         });
83588                 }
83589             };
83590
83591
83592             check.entityIDs = function(val) {
83593                 if (!arguments.length) return _entityIDs;
83594                 _entityIDs = val;
83595                 return check;
83596             };
83597
83598
83599             check.tags = function(tags) {
83600
83601                 _tags = tags;
83602
83603                 function isChecked(val) {
83604                     return val !== 'no' && val !== '' && val !== undefined && val !== null;
83605                 }
83606
83607                 function textFor(val) {
83608                     if (val === '') val = undefined;
83609                     var index = values.indexOf(val);
83610                     return (index !== -1 ? texts[index] : ('"' + val + '"'));
83611                 }
83612
83613                 checkImpliedYes();
83614
83615                 var isMixed = Array.isArray(tags[field.key]);
83616
83617                 _value = !isMixed && tags[field.key] && tags[field.key].toLowerCase();
83618
83619                 if (field.type === 'onewayCheck' && (_value === '1' || _value === '-1')) {
83620                     _value = 'yes';
83621                 }
83622
83623                 input
83624                     .property('indeterminate', isMixed || (field.type !== 'defaultCheck' && !_value))
83625                     .property('checked', isChecked(_value));
83626
83627                 text
83628                     .text(isMixed ? _t('inspector.multiple_values') : textFor(_value))
83629                     .classed('mixed', isMixed);
83630
83631                 label
83632                     .classed('set', !!_value);
83633
83634                 if (field.type === 'onewayCheck') {
83635                     reverser
83636                         .classed('hide', reverserHidden())
83637                         .call(reverserSetText);
83638                 }
83639             };
83640
83641
83642             check.focus = function() {
83643                 input.node().focus();
83644             };
83645
83646             return utilRebind(check, dispatch$1, 'on');
83647         }
83648
83649         function uiFieldCombo(field, context) {
83650             var dispatch$1 = dispatch('change');
83651             var taginfo = services.taginfo;
83652             var isMulti = (field.type === 'multiCombo');
83653             var isNetwork = (field.type === 'networkCombo');
83654             var isSemi = (field.type === 'semiCombo');
83655             var optstrings = field.strings && field.strings.options;
83656             var optarray = field.options;
83657             var snake_case = (field.snake_case || (field.snake_case === undefined));
83658             var caseSensitive = field.caseSensitive;
83659             var combobox = uiCombobox(context, 'combo-' + field.safeid)
83660                 .caseSensitive(caseSensitive)
83661                 .minItems(isMulti || isSemi ? 1 : 2);
83662             var container = select(null);
83663             var inputWrap = select(null);
83664             var input = select(null);
83665             var _comboData = [];
83666             var _multiData = [];
83667             var _entityIDs = [];
83668             var _tags;
83669             var _countryCode;
83670             var _staticPlaceholder;
83671
83672             // initialize deprecated tags array
83673             var _dataDeprecated = [];
83674             _mainFileFetcher.get('deprecated')
83675                 .then(function(d) { _dataDeprecated = d; })
83676                 .catch(function() { /* ignore */ });
83677
83678
83679             // ensure multiCombo field.key ends with a ':'
83680             if (isMulti && /[^:]$/.test(field.key)) {
83681                 field.key += ':';
83682             }
83683
83684
83685             function snake(s) {
83686                 return s.replace(/\s+/g, '_');
83687             }
83688
83689             function unsnake(s) {
83690                 return s.replace(/_+/g, ' ');
83691             }
83692
83693             function clean(s) {
83694                 return s.split(';')
83695                     .map(function(s) { return s.trim(); })
83696                     .join(';');
83697             }
83698
83699
83700             // returns the tag value for a display value
83701             // (for multiCombo, dval should be the key suffix, not the entire key)
83702             function tagValue(dval) {
83703                 dval = clean(dval || '');
83704
83705                 if (optstrings) {
83706                     var found = _comboData.find(function(o) {
83707                         return o.key && clean(o.value) === dval;
83708                     });
83709                     if (found) {
83710                         return found.key;
83711                     }
83712                 }
83713
83714                 if (field.type === 'typeCombo' && !dval) {
83715                     return 'yes';
83716                 }
83717
83718                 return (snake_case ? snake(dval) : dval) || undefined;
83719             }
83720
83721
83722             // returns the display value for a tag value
83723             // (for multiCombo, tval should be the key suffix, not the entire key)
83724             function displayValue(tval) {
83725                 tval = tval || '';
83726
83727                 if (optstrings) {
83728                     var found = _comboData.find(function(o) {
83729                         return o.key === tval && o.value;
83730                     });
83731                     if (found) {
83732                         return found.value;
83733                     }
83734                 }
83735
83736                 if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
83737                     return '';
83738                 }
83739
83740                 return snake_case ? unsnake(tval) : tval;
83741             }
83742
83743
83744             // Compute the difference between arrays of objects by `value` property
83745             //
83746             // objectDifference([{value:1}, {value:2}, {value:3}], [{value:2}])
83747             // > [{value:1}, {value:3}]
83748             //
83749             function objectDifference(a, b) {
83750                 return a.filter(function(d1) {
83751                     return !b.some(function(d2) {
83752                         return !d2.isMixed && d1.value === d2.value;
83753                     });
83754                 });
83755             }
83756
83757
83758             function initCombo(selection, attachTo) {
83759                 if (optstrings) {
83760                     selection.attr('readonly', 'readonly');
83761                     selection.call(combobox, attachTo);
83762                     setStaticValues(setPlaceholder);
83763
83764                 } else if (optarray) {
83765                     selection.call(combobox, attachTo);
83766                     setStaticValues(setPlaceholder);
83767
83768                 } else if (taginfo) {
83769                     selection.call(combobox.fetcher(setTaginfoValues), attachTo);
83770                     setTaginfoValues('', setPlaceholder);
83771                 }
83772             }
83773
83774
83775             function setStaticValues(callback) {
83776                 if (!(optstrings || optarray)) return;
83777
83778                 if (optstrings) {
83779                     _comboData = Object.keys(optstrings).map(function(k) {
83780                         var v = field.t('options.' + k, { 'default': optstrings[k] });
83781                         return {
83782                             key: k,
83783                             value: v,
83784                             title: v
83785                         };
83786                     });
83787
83788                 } else if (optarray) {
83789                     _comboData = optarray.map(function(k) {
83790                         var v = snake_case ? unsnake(k) : k;
83791                         return {
83792                             key: k,
83793                             value: v,
83794                             title: v
83795                         };
83796                     });
83797                 }
83798
83799                 combobox.data(objectDifference(_comboData, _multiData));
83800                 if (callback) callback(_comboData);
83801             }
83802
83803
83804             function setTaginfoValues(q, callback) {
83805                 var fn = isMulti ? 'multikeys' : 'values';
83806                 var query = (isMulti ? field.key : '') + q;
83807                 var hasCountryPrefix = isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0;
83808                 if (hasCountryPrefix) {
83809                     query = _countryCode + ':';
83810                 }
83811
83812                 var params = {
83813                     debounce: (q !== ''),
83814                     key: field.key,
83815                     query: query
83816                 };
83817
83818                 if (_entityIDs.length) {
83819                     params.geometry = context.graph().geometry(_entityIDs[0]);
83820                 }
83821
83822                 taginfo[fn](params, function(err, data) {
83823                     if (err) return;
83824
83825                     data = data.filter(function(d) {
83826
83827                         if (field.type === 'typeCombo' && d.value === 'yes') {
83828                             // don't show the fallback value
83829                             return false;
83830                         }
83831
83832                         // don't show values with very low usage
83833                         return !d.count || d.count > 10;
83834                     });
83835
83836                     var deprecatedValues = osmEntity.deprecatedTagValuesByKey(_dataDeprecated)[field.key];
83837                     if (deprecatedValues) {
83838                         // don't suggest deprecated tag values
83839                         data = data.filter(function(d) {
83840                             return deprecatedValues.indexOf(d.value) === -1;
83841                         });
83842                     }
83843
83844                     if (hasCountryPrefix) {
83845                         data = data.filter(function(d) {
83846                             return d.value.toLowerCase().indexOf(_countryCode + ':') === 0;
83847                         });
83848                     }
83849
83850                     // hide the caret if there are no suggestions
83851                     container.classed('empty-combobox', data.length === 0);
83852
83853                     _comboData = data.map(function(d) {
83854                         var k = d.value;
83855                         if (isMulti) k = k.replace(field.key, '');
83856                         var v = snake_case ? unsnake(k) : k;
83857                         return {
83858                             key: k,
83859                             value: v,
83860                             title: isMulti ? v : d.title
83861                         };
83862                     });
83863
83864                     _comboData = objectDifference(_comboData, _multiData);
83865                     if (callback) callback(_comboData);
83866                 });
83867             }
83868
83869
83870             function setPlaceholder(values) {
83871
83872                 if (isMulti || isSemi) {
83873                     _staticPlaceholder = field.placeholder() || _t('inspector.add');
83874                 } else {
83875                     var vals = values
83876                         .map(function(d) { return d.value; })
83877                         .filter(function(s) { return s.length < 20; });
83878
83879                     var placeholders = vals.length > 1 ? vals : values.map(function(d) { return d.key; });
83880                     _staticPlaceholder = field.placeholder() || placeholders.slice(0, 3).join(', ');
83881                 }
83882
83883                 if (!/(…|\.\.\.)$/.test(_staticPlaceholder)) {
83884                     _staticPlaceholder += '…';
83885                 }
83886
83887                 var ph;
83888                 if (!isMulti && !isSemi && _tags && Array.isArray(_tags[field.key])) {
83889                     ph = _t('inspector.multiple_values');
83890                 } else {
83891                     ph =  _staticPlaceholder;
83892                 }
83893
83894                 container.selectAll('input')
83895                     .attr('placeholder', ph);
83896             }
83897
83898
83899             function change() {
83900                 var t = {};
83901                 var val;
83902
83903                 if (isMulti || isSemi) {
83904                     val = tagValue(utilGetSetValue(input).replace(/,/g, ';')) || '';
83905                     container.classed('active', false);
83906                     utilGetSetValue(input, '');
83907
83908                     var vals = val.split(';').filter(Boolean);
83909                     if (!vals.length) return;
83910
83911                     if (isMulti) {
83912                         utilArrayUniq(vals).forEach(function(v) {
83913                             var key = field.key + v;
83914                             if (_tags) {
83915                                 // don't set a multicombo value to 'yes' if it already has a non-'no' value
83916                                 // e.g. `language:de=main`
83917                                 var old = _tags[key];
83918                                 if (typeof old === 'string' && old.toLowerCase() !== 'no') return;
83919                             }
83920                             key = context.cleanTagKey(key);
83921                             field.keys.push(key);
83922                             t[key] = 'yes';
83923                         });
83924
83925                     } else if (isSemi) {
83926                         var arr = _multiData.map(function(d) { return d.key; });
83927                         arr = arr.concat(vals);
83928                         t[field.key] = context.cleanTagValue(utilArrayUniq(arr).filter(Boolean).join(';'));
83929                     }
83930
83931                     window.setTimeout(function() { input.node().focus(); }, 10);
83932
83933                 } else {
83934                     var rawValue = utilGetSetValue(input);
83935
83936                     // don't override multiple values with blank string
83937                     if (!rawValue && Array.isArray(_tags[field.key])) return;
83938
83939                     val = context.cleanTagValue(tagValue(rawValue));
83940                     t[field.key] = val || undefined;
83941                 }
83942
83943                 dispatch$1.call('change', this, t);
83944             }
83945
83946
83947             function removeMultikey(d) {
83948                 event.stopPropagation();
83949                 var t = {};
83950                 if (isMulti) {
83951                     t[d.key] = undefined;
83952                 } else if (isSemi) {
83953                     var arr = _multiData.map(function(md) {
83954                         return md.key === d.key ? null : md.key;
83955                     }).filter(Boolean);
83956
83957                     arr = utilArrayUniq(arr);
83958                     t[field.key] = arr.length ? arr.join(';') : undefined;
83959                 }
83960                 dispatch$1.call('change', this, t);
83961             }
83962
83963
83964             function combo(selection) {
83965                 container = selection.selectAll('.form-field-input-wrap')
83966                     .data([0]);
83967
83968                 var type = (isMulti || isSemi) ? 'multicombo': 'combo';
83969                 container = container.enter()
83970                     .append('div')
83971                     .attr('class', 'form-field-input-wrap form-field-input-' + type)
83972                     .merge(container);
83973
83974                 if (isMulti || isSemi) {
83975                     container = container.selectAll('.chiplist')
83976                         .data([0]);
83977
83978                     var listClass = 'chiplist';
83979
83980                     // Use a separate line for each value in the Destinations field
83981                     // to mimic highway exit signs
83982                     if (field.key === 'destination') {
83983                         listClass += ' full-line-chips';
83984                     }
83985
83986                     container = container.enter()
83987                         .append('ul')
83988                         .attr('class', listClass)
83989                         .on('click', function() {
83990                             window.setTimeout(function() { input.node().focus(); }, 10);
83991                         })
83992                         .merge(container);
83993
83994
83995                     inputWrap = container.selectAll('.input-wrap')
83996                         .data([0]);
83997
83998                     inputWrap = inputWrap.enter()
83999                         .append('li')
84000                         .attr('class', 'input-wrap')
84001                         .merge(inputWrap);
84002
84003                     input = inputWrap.selectAll('input')
84004                         .data([0]);
84005                 } else {
84006                     input = container.selectAll('input')
84007                         .data([0]);
84008                 }
84009
84010                 input = input.enter()
84011                     .append('input')
84012                     .attr('type', 'text')
84013                     .attr('id', field.domId)
84014                     .call(utilNoAuto)
84015                     .call(initCombo, selection)
84016                     .merge(input);
84017
84018                 if (isNetwork) {
84019                     var extent = combinedEntityExtent();
84020                     var countryCode = extent && iso1A2Code(extent.center());
84021                     _countryCode = countryCode && countryCode.toLowerCase();
84022                 }
84023
84024                 input
84025                     .on('change', change)
84026                     .on('blur', change);
84027
84028                 input
84029                     .on('keydown.field', function() {
84030                         switch (event.keyCode) {
84031                             case 13: // ↩ Return
84032                                 input.node().blur(); // blurring also enters the value
84033                                 event.stopPropagation();
84034                                 break;
84035                         }
84036                     });
84037
84038                 if (isMulti || isSemi) {
84039                     combobox
84040                         .on('accept', function() {
84041                             input.node().blur();
84042                             input.node().focus();
84043                         });
84044
84045                     input
84046                         .on('focus', function() { container.classed('active', true); });
84047                 }
84048             }
84049
84050
84051             combo.tags = function(tags) {
84052                 _tags = tags;
84053
84054                 if (isMulti || isSemi) {
84055                     _multiData = [];
84056
84057                     var maxLength;
84058
84059                     if (isMulti) {
84060                         // Build _multiData array containing keys already set..
84061                         for (var k in tags) {
84062                             if (k.indexOf(field.key) !== 0) continue;
84063                             var v = tags[k];
84064                             if (!v || (typeof v === 'string' && v.toLowerCase() === 'no')) continue;
84065
84066                             var suffix = k.substring(field.key.length);
84067                             _multiData.push({
84068                                 key: k,
84069                                 value: displayValue(suffix),
84070                                 isMixed: Array.isArray(v)
84071                             });
84072                         }
84073
84074                         // Set keys for form-field modified (needed for undo and reset buttons)..
84075                         field.keys = _multiData.map(function(d) { return d.key; });
84076
84077                         // limit the input length so it fits after prepending the key prefix
84078                         maxLength = context.maxCharsForTagKey() - utilUnicodeCharsCount(field.key);
84079
84080                     } else if (isSemi) {
84081
84082                         var allValues = [];
84083                         var commonValues;
84084                         if (Array.isArray(tags[field.key])) {
84085
84086                             tags[field.key].forEach(function(tagVal) {
84087                                 var thisVals = utilArrayUniq((tagVal || '').split(';')).filter(Boolean);
84088                                 allValues = allValues.concat(thisVals);
84089                                 if (!commonValues) {
84090                                     commonValues = thisVals;
84091                                 } else {
84092                                     commonValues = commonValues.filter(value => thisVals.includes(value));
84093                                 }
84094                             });
84095                             allValues = utilArrayUniq(allValues).filter(Boolean);
84096
84097                         } else {
84098                             allValues =  utilArrayUniq((tags[field.key] || '').split(';')).filter(Boolean);
84099                             commonValues = allValues;
84100                         }
84101
84102                         _multiData = allValues.map(function(v) {
84103                             return {
84104                                 key: v,
84105                                 value: displayValue(v),
84106                                 isMixed: !commonValues.includes(v)
84107                             };
84108                         });
84109
84110                         var currLength = utilUnicodeCharsCount(commonValues.join(';'));
84111
84112                         // limit the input length to the remaining available characters
84113                         maxLength = context.maxCharsForTagValue() - currLength;
84114
84115                         if (currLength > 0) {
84116                             // account for the separator if a new value will be appended to existing
84117                             maxLength -= 1;
84118                         }
84119                     }
84120                     // a negative maxlength doesn't make sense
84121                     maxLength = Math.max(0, maxLength);
84122
84123                     var allowDragAndDrop = isSemi // only semiCombo values are ordered
84124                         && !Array.isArray(tags[field.key]);
84125
84126                     // Exclude existing multikeys from combo options..
84127                     var available = objectDifference(_comboData, _multiData);
84128                     combobox.data(available);
84129
84130                     // Hide 'Add' button if this field uses fixed set of
84131                     // translateable optstrings and they're all currently used,
84132                     // or if the field is already at its character limit
84133                     var hideAdd = (optstrings && !available.length) || maxLength <= 0;
84134                     container.selectAll('.chiplist .input-wrap')
84135                         .style('display', hideAdd ? 'none' : null);
84136
84137
84138                     // Render chips
84139                     var chips = container.selectAll('.chip')
84140                         .data(_multiData);
84141
84142                     chips.exit()
84143                         .remove();
84144
84145                     var enter = chips.enter()
84146                         .insert('li', '.input-wrap')
84147                         .attr('class', 'chip');
84148
84149                     enter.append('span');
84150                     enter.append('a');
84151
84152                     chips = chips.merge(enter)
84153                         .order()
84154                         .classed('draggable', allowDragAndDrop)
84155                         .classed('mixed', function(d) {
84156                             return d.isMixed;
84157                         })
84158                         .attr('title', function(d) {
84159                             return d.isMixed ? _t('inspector.unshared_value_tooltip') : null;
84160                         });
84161
84162                     if (allowDragAndDrop) {
84163                         registerDragAndDrop(chips);
84164                     }
84165
84166                     chips.select('span')
84167                         .text(function(d) { return d.value; });
84168
84169                     chips.select('a')
84170                         .on('click', removeMultikey)
84171                         .attr('class', 'remove')
84172                         .text('×');
84173
84174                 } else {
84175                     var isMixed = Array.isArray(tags[field.key]);
84176
84177                     var mixedValues = isMixed && tags[field.key].map(function(val) {
84178                         return displayValue(val);
84179                     }).filter(Boolean);
84180
84181                     utilGetSetValue(input, !isMixed ? displayValue(tags[field.key]) : '')
84182                         .attr('title', isMixed ? mixedValues.join('\n') : undefined)
84183                         .attr('placeholder', isMixed ? _t('inspector.multiple_values') : _staticPlaceholder || '')
84184                         .classed('mixed', isMixed);
84185                 }
84186             };
84187
84188             function registerDragAndDrop(selection) {
84189
84190                 // allow drag and drop re-ordering of chips
84191                 var dragOrigin, targetIndex;
84192                 selection.call(d3_drag()
84193                     .on('start', function() {
84194                         dragOrigin = {
84195                             x: event.x,
84196                             y: event.y
84197                         };
84198                         targetIndex = null;
84199                     })
84200                     .on('drag', function(d, index) {
84201                         var x = event.x - dragOrigin.x,
84202                             y = event.y - dragOrigin.y;
84203
84204                         if (!select(this).classed('dragging') &&
84205                             // don't display drag until dragging beyond a distance threshold
84206                             Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
84207
84208                         select(this)
84209                             .classed('dragging', true);
84210
84211                         targetIndex = null;
84212                         var targetIndexOffsetTop = null;
84213                         var draggedTagWidth = select(this).node().offsetWidth;
84214
84215                         if (field.key === 'destination') { // meaning tags are full width
84216                             container.selectAll('.chip')
84217                                 .style('transform', function(d2, index2) {
84218                                     var node = select(this).node();
84219
84220                                     if (index === index2) {
84221                                         return 'translate(' + x + 'px, ' + y + 'px)';
84222                                     // move the dragged tag up the order
84223                                     } else if (index2 > index && event.y > node.offsetTop) {
84224                                         if (targetIndex === null || index2 > targetIndex) {
84225                                             targetIndex = index2;
84226                                         }
84227                                         return 'translateY(-100%)';
84228                                     // move the dragged tag down the order
84229                                     } else if (index2 < index && event.y < node.offsetTop + node.offsetHeight) {
84230                                         if (targetIndex === null || index2 < targetIndex) {
84231                                             targetIndex = index2;
84232                                         }
84233                                         return 'translateY(100%)';
84234                                     }
84235                                     return null;
84236                                 });
84237                         } else {
84238                             container.selectAll('.chip')
84239                                 .each(function(d2, index2) {
84240                                     var node = select(this).node();
84241
84242                                     // check the cursor is in the bounding box
84243                                     if (
84244                                         index !== index2 &&
84245                                         event.x < node.offsetLeft + node.offsetWidth + 5 &&
84246                                         event.x > node.offsetLeft &&
84247                                         event.y < node.offsetTop + node.offsetHeight &&
84248                                         event.y > node.offsetTop
84249                                     ) {
84250                                         targetIndex = index2;
84251                                         targetIndexOffsetTop = node.offsetTop;
84252                                     }
84253                                 })
84254                                 .style('transform', function(d2, index2) {
84255                                     var node = select(this).node();
84256
84257                                     if (index === index2) {
84258                                         return 'translate(' + x + 'px, ' + y + 'px)';
84259                                     }
84260
84261                                     // only translate tags in the same row
84262                                     if (node.offsetTop === targetIndexOffsetTop) {
84263                                         if (index2 < index && index2 >= targetIndex) {
84264                                             return 'translateX(' + draggedTagWidth + 'px)';
84265                                         } else if (index2 > index && index2 <= targetIndex) {
84266                                             return 'translateX(-' + draggedTagWidth + 'px)';
84267                                         }
84268                                     }
84269                                     return null;
84270                                 });
84271                             }
84272                     })
84273                     .on('end', function(d, index) {
84274                         if (!select(this).classed('dragging')) {
84275                             return;
84276                         }
84277
84278                         select(this)
84279                             .classed('dragging', false);
84280
84281                         container.selectAll('.chip')
84282                             .style('transform', null);
84283
84284                         if (typeof targetIndex === 'number') {
84285                             var element = _multiData[index];
84286                             _multiData.splice(index, 1);
84287                             _multiData.splice(targetIndex, 0, element);
84288
84289                             var t = {};
84290
84291                             if (_multiData.length) {
84292                                 t[field.key] = _multiData.map(function(element) {
84293                                     return element.key;
84294                                 }).join(';');
84295                             } else {
84296                                 t[field.key] = undefined;
84297                             }
84298
84299                             dispatch$1.call('change', this, t);
84300                         }
84301                         dragOrigin = undefined;
84302                         targetIndex = undefined;
84303                     })
84304                 );
84305             }
84306
84307
84308             combo.focus = function() {
84309                 input.node().focus();
84310             };
84311
84312
84313             combo.entityIDs = function(val) {
84314                 if (!arguments.length) return _entityIDs;
84315                 _entityIDs = val;
84316                 return combo;
84317             };
84318
84319
84320             function combinedEntityExtent() {
84321                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84322             }
84323
84324
84325             return utilRebind(combo, dispatch$1, 'on');
84326         }
84327
84328         function uiFieldText(field, context) {
84329             var dispatch$1 = dispatch('change');
84330             var input = select(null);
84331             var outlinkButton = select(null);
84332             var _entityIDs = [];
84333             var _tags;
84334             var _phoneFormats = {};
84335
84336             if (field.type === 'tel') {
84337                 _mainFileFetcher.get('phone_formats')
84338                     .then(function(d) {
84339                         _phoneFormats = d;
84340                         updatePhonePlaceholder();
84341                     })
84342                     .catch(function() { /* ignore */ });
84343             }
84344
84345             function i(selection) {
84346                 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
84347                 var preset = entity && _mainPresetIndex.match(entity, context.graph());
84348                 var isLocked = preset && preset.suggestion && field.id === 'brand';
84349                 field.locked(isLocked);
84350
84351                 var wrap = selection.selectAll('.form-field-input-wrap')
84352                     .data([0]);
84353
84354                 wrap = wrap.enter()
84355                     .append('div')
84356                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
84357                     .merge(wrap);
84358
84359                 input = wrap.selectAll('input')
84360                     .data([0]);
84361
84362                 input = input.enter()
84363                     .append('input')
84364                     .attr('type', field.type === 'identifier' ? 'text' : field.type)
84365                     .attr('id', field.domId)
84366                     .classed(field.type, true)
84367                     .call(utilNoAuto)
84368                     .merge(input);
84369
84370                 input
84371                     .classed('disabled', !!isLocked)
84372                     .attr('readonly', isLocked || null)
84373                     .on('input', change(true))
84374                     .on('blur', change())
84375                     .on('change', change());
84376
84377
84378                 if (field.type === 'tel') {
84379                     updatePhonePlaceholder();
84380
84381                 } else if (field.type === 'number') {
84382                     var rtl = (_mainLocalizer.textDirection() === 'rtl');
84383
84384                     input.attr('type', 'text');
84385
84386                     var buttons = wrap.selectAll('.increment, .decrement')
84387                         .data(rtl ? [1, -1] : [-1, 1]);
84388
84389                     buttons.enter()
84390                         .append('button')
84391                         .attr('tabindex', -1)
84392                         .attr('class', function(d) {
84393                             var which = (d === 1 ? 'increment' : 'decrement');
84394                             return 'form-field-button ' + which;
84395                         })
84396                         .merge(buttons)
84397                         .on('click', function(d) {
84398                             event.preventDefault();
84399                             var raw_vals = input.node().value || '0';
84400                             var vals = raw_vals.split(';');
84401                             vals = vals.map(function(v) {
84402                                 var num = parseFloat(v.trim(), 10);
84403                                 return isFinite(num) ? clamped(num + d) : v.trim();
84404                             });
84405                             input.node().value = vals.join(';');
84406                             change()();
84407                         });
84408                 } else if (field.type === 'identifier' && field.urlFormat && field.pattern) {
84409
84410                     input.attr('type', 'text');
84411
84412                     outlinkButton = wrap.selectAll('.foreign-id-permalink')
84413                         .data([0]);
84414
84415                     outlinkButton.enter()
84416                         .append('button')
84417                         .attr('tabindex', -1)
84418                         .call(svgIcon('#iD-icon-out-link'))
84419                         .attr('class', 'form-field-button foreign-id-permalink')
84420                         .attr('title', function() {
84421                             var domainResults = /^https?:\/\/(.{1,}?)\//.exec(field.urlFormat);
84422                             if (domainResults.length >= 2 && domainResults[1]) {
84423                                 var domain = domainResults[1];
84424                                 return _t('icons.view_on', { domain: domain });
84425                             }
84426                             return '';
84427                         })
84428                         .on('click', function() {
84429                             event.preventDefault();
84430
84431                             var value = validIdentifierValueForLink();
84432                             if (value) {
84433                                 var url = field.urlFormat.replace(/{value}/, encodeURIComponent(value));
84434                                 window.open(url, '_blank');
84435                             }
84436                         })
84437                         .merge(outlinkButton);
84438                 }
84439             }
84440
84441
84442             function updatePhonePlaceholder() {
84443                 if (input.empty() || !Object.keys(_phoneFormats).length) return;
84444
84445                 var extent = combinedEntityExtent();
84446                 var countryCode = extent && iso1A2Code(extent.center());
84447                 var format = countryCode && _phoneFormats[countryCode.toLowerCase()];
84448                 if (format) input.attr('placeholder', format);
84449             }
84450
84451
84452             function validIdentifierValueForLink() {
84453                 if (field.type === 'identifier' && field.pattern) {
84454                     var value = utilGetSetValue(input).trim().split(';')[0];
84455                     return value && value.match(new RegExp(field.pattern));
84456                 }
84457                 return null;
84458             }
84459
84460
84461             // clamp number to min/max
84462             function clamped(num) {
84463                 if (field.minValue !== undefined) {
84464                     num = Math.max(num, field.minValue);
84465                 }
84466                 if (field.maxValue !== undefined) {
84467                     num = Math.min(num, field.maxValue);
84468                 }
84469                 return num;
84470             }
84471
84472
84473             function change(onInput) {
84474                 return function() {
84475                     var t = {};
84476                     var val = utilGetSetValue(input);
84477                     if (!onInput) val = context.cleanTagValue(val);
84478
84479                     // don't override multiple values with blank string
84480                     if (!val && Array.isArray(_tags[field.key])) return;
84481
84482                     if (!onInput) {
84483                         if (field.type === 'number' && val) {
84484                             var vals = val.split(';');
84485                             vals = vals.map(function(v) {
84486                                 var num = parseFloat(v.trim(), 10);
84487                                 return isFinite(num) ? clamped(num) : v.trim();
84488                             });
84489                             val = vals.join(';');
84490                         }
84491                         utilGetSetValue(input, val);
84492                     }
84493                     t[field.key] = val || undefined;
84494                     dispatch$1.call('change', this, t, onInput);
84495                 };
84496             }
84497
84498
84499             i.entityIDs = function(val) {
84500                 if (!arguments.length) return _entityIDs;
84501                 _entityIDs = val;
84502                 return i;
84503             };
84504
84505
84506             i.tags = function(tags) {
84507                 _tags = tags;
84508
84509                 var isMixed = Array.isArray(tags[field.key]);
84510
84511                 utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '')
84512                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
84513                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : (field.placeholder() || _t('inspector.unknown')))
84514                     .classed('mixed', isMixed);
84515
84516                 if (outlinkButton && !outlinkButton.empty()) {
84517                     var disabled = !validIdentifierValueForLink();
84518                     outlinkButton.classed('disabled', disabled);
84519                 }
84520             };
84521
84522
84523             i.focus = function() {
84524                 var node = input.node();
84525                 if (node) node.focus();
84526             };
84527
84528             function combinedEntityExtent() {
84529                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84530             }
84531
84532             return utilRebind(i, dispatch$1, 'on');
84533         }
84534
84535         function uiFieldAccess(field, context) {
84536             var dispatch$1 = dispatch('change');
84537             var items = select(null);
84538             var _tags;
84539
84540             function access(selection) {
84541                 var wrap = selection.selectAll('.form-field-input-wrap')
84542                     .data([0]);
84543
84544                 wrap = wrap.enter()
84545                     .append('div')
84546                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
84547                     .merge(wrap);
84548
84549                 var list = wrap.selectAll('ul')
84550                     .data([0]);
84551
84552                 list = list.enter()
84553                     .append('ul')
84554                     .attr('class', 'rows')
84555                     .merge(list);
84556
84557
84558                 items = list.selectAll('li')
84559                     .data(field.keys);
84560
84561                 // Enter
84562                 var enter = items.enter()
84563                     .append('li')
84564                     .attr('class', function(d) { return 'labeled-input preset-access-' + d; });
84565
84566                 enter
84567                     .append('span')
84568                     .attr('class', 'label preset-label-access')
84569                     .attr('for', function(d) { return 'preset-input-access-' + d; })
84570                     .text(function(d) { return field.t('types.' + d); });
84571
84572                 enter
84573                     .append('div')
84574                     .attr('class', 'preset-input-access-wrap')
84575                     .append('input')
84576                     .attr('type', 'text')
84577                     .attr('class', function(d) { return 'preset-input-access preset-input-access-' + d; })
84578                     .call(utilNoAuto)
84579                     .each(function(d) {
84580                         select(this)
84581                             .call(uiCombobox(context, 'access-' + d)
84582                                 .data(access.options(d))
84583                             );
84584                     });
84585
84586
84587                 // Update
84588                 items = items.merge(enter);
84589
84590                 wrap.selectAll('.preset-input-access')
84591                     .on('change', change)
84592                     .on('blur', change);
84593             }
84594
84595
84596             function change(d) {
84597                 var tag = {};
84598                 var value = context.cleanTagValue(utilGetSetValue(select(this)));
84599
84600                 // don't override multiple values with blank string
84601                 if (!value && typeof _tags[d] !== 'string') return;
84602
84603                 tag[d] = value || undefined;
84604                 dispatch$1.call('change', this, tag);
84605             }
84606
84607
84608             access.options = function(type) {
84609                 var options = ['no', 'permissive', 'private', 'permit', 'destination'];
84610
84611                 if (type !== 'access') {
84612                     options.unshift('yes');
84613                     options.push('designated');
84614
84615                     if (type === 'bicycle') {
84616                         options.push('dismount');
84617                     }
84618                 }
84619
84620                 return options.map(function(option) {
84621                     return {
84622                         title: field.t('options.' + option + '.description'),
84623                         value: option
84624                     };
84625                 });
84626             };
84627
84628
84629             var placeholdersByHighway = {
84630                 footway: {
84631                     foot: 'designated',
84632                     motor_vehicle: 'no'
84633                 },
84634                 steps: {
84635                     foot: 'yes',
84636                     motor_vehicle: 'no',
84637                     bicycle: 'no',
84638                     horse: 'no'
84639                 },
84640                 pedestrian: {
84641                     foot: 'yes',
84642                     motor_vehicle: 'no'
84643                 },
84644                 cycleway: {
84645                     motor_vehicle: 'no',
84646                     bicycle: 'designated'
84647                 },
84648                 bridleway: {
84649                     motor_vehicle: 'no',
84650                     horse: 'designated'
84651                 },
84652                 path: {
84653                     foot: 'yes',
84654                     motor_vehicle: 'no',
84655                     bicycle: 'yes',
84656                     horse: 'yes'
84657                 },
84658                 motorway: {
84659                     foot: 'no',
84660                     motor_vehicle: 'yes',
84661                     bicycle: 'no',
84662                     horse: 'no'
84663                 },
84664                 trunk: {
84665                     motor_vehicle: 'yes'
84666                 },
84667                 primary: {
84668                     foot: 'yes',
84669                     motor_vehicle: 'yes',
84670                     bicycle: 'yes',
84671                     horse: 'yes'
84672                 },
84673                 secondary: {
84674                     foot: 'yes',
84675                     motor_vehicle: 'yes',
84676                     bicycle: 'yes',
84677                     horse: 'yes'
84678                 },
84679                 tertiary: {
84680                     foot: 'yes',
84681                     motor_vehicle: 'yes',
84682                     bicycle: 'yes',
84683                     horse: 'yes'
84684                 },
84685                 residential: {
84686                     foot: 'yes',
84687                     motor_vehicle: 'yes',
84688                     bicycle: 'yes',
84689                     horse: 'yes'
84690                 },
84691                 unclassified: {
84692                     foot: 'yes',
84693                     motor_vehicle: 'yes',
84694                     bicycle: 'yes',
84695                     horse: 'yes'
84696                 },
84697                 service: {
84698                     foot: 'yes',
84699                     motor_vehicle: 'yes',
84700                     bicycle: 'yes',
84701                     horse: 'yes'
84702                 },
84703                 motorway_link: {
84704                     foot: 'no',
84705                     motor_vehicle: 'yes',
84706                     bicycle: 'no',
84707                     horse: 'no'
84708                 },
84709                 trunk_link: {
84710                     motor_vehicle: 'yes'
84711                 },
84712                 primary_link: {
84713                     foot: 'yes',
84714                     motor_vehicle: 'yes',
84715                     bicycle: 'yes',
84716                     horse: 'yes'
84717                 },
84718                 secondary_link: {
84719                     foot: 'yes',
84720                     motor_vehicle: 'yes',
84721                     bicycle: 'yes',
84722                     horse: 'yes'
84723                 },
84724                 tertiary_link: {
84725                     foot: 'yes',
84726                     motor_vehicle: 'yes',
84727                     bicycle: 'yes',
84728                     horse: 'yes'
84729                 }
84730             };
84731
84732
84733             access.tags = function(tags) {
84734                 _tags = tags;
84735
84736                 utilGetSetValue(items.selectAll('.preset-input-access'), function(d) {
84737                         return typeof tags[d] === 'string' ? tags[d] : '';
84738                     })
84739                     .classed('mixed', function(d) {
84740                         return tags[d] && Array.isArray(tags[d]);
84741                     })
84742                     .attr('title', function(d) {
84743                         return tags[d] && Array.isArray(tags[d]) && tags[d].filter(Boolean).join('\n');
84744                     })
84745                     .attr('placeholder', function(d) {
84746                         if (tags[d] && Array.isArray(tags[d])) {
84747                             return _t('inspector.multiple_values');
84748                         }
84749                         if (d === 'access') {
84750                             return 'yes';
84751                         }
84752                         if (tags.access && typeof tags.access === 'string') {
84753                             return tags.access;
84754                         }
84755                         if (tags.highway) {
84756                             if (typeof tags.highway === 'string') {
84757                                 if (placeholdersByHighway[tags.highway] &&
84758                                     placeholdersByHighway[tags.highway][d]) {
84759
84760                                     return placeholdersByHighway[tags.highway][d];
84761                                 }
84762                             } else {
84763                                 var impliedAccesses = tags.highway.filter(Boolean).map(function(highwayVal) {
84764                                     return placeholdersByHighway[highwayVal] && placeholdersByHighway[highwayVal][d];
84765                                 }).filter(Boolean);
84766
84767                                 if (impliedAccesses.length === tags.highway.length &&
84768                                     new Set(impliedAccesses).size === 1) {
84769                                     // if all the highway values have the same implied access for this type then use that
84770                                     return impliedAccesses[0];
84771                                 }
84772                             }
84773                         }
84774                         return field.placeholder();
84775                     });
84776             };
84777
84778
84779             access.focus = function() {
84780                 items.selectAll('.preset-input-access')
84781                     .node().focus();
84782             };
84783
84784
84785             return utilRebind(access, dispatch$1, 'on');
84786         }
84787
84788         function uiFieldAddress(field, context) {
84789             var dispatch$1 = dispatch('change');
84790             var _selection = select(null);
84791             var _wrap = select(null);
84792             var addrField = _mainPresetIndex.field('address');   // needed for placeholder strings
84793
84794             var _entityIDs = [];
84795             var _tags;
84796             var _countryCode;
84797             var _addressFormats = [{
84798                 format: [
84799                     ['housenumber', 'street'],
84800                     ['city', 'postcode']
84801                 ]
84802               }];
84803
84804             _mainFileFetcher.get('address_formats')
84805                 .then(function(d) {
84806                     _addressFormats = d;
84807                     if (!_selection.empty()) {
84808                         _selection.call(address);
84809                     }
84810                 })
84811                 .catch(function() { /* ignore */ });
84812
84813
84814             function getNearStreets() {
84815                 var extent = combinedEntityExtent();
84816                 var l = extent.center();
84817                 var box = geoExtent(l).padByMeters(200);
84818
84819                 var streets = context.history().intersects(box)
84820                     .filter(isAddressable)
84821                     .map(function(d) {
84822                         var loc = context.projection([
84823                             (extent[0][0] + extent[1][0]) / 2,
84824                             (extent[0][1] + extent[1][1]) / 2
84825                         ]);
84826                         var choice = geoChooseEdge(context.graph().childNodes(d), loc, context.projection);
84827
84828                         return {
84829                             title: d.tags.name,
84830                             value: d.tags.name,
84831                             dist: choice.distance
84832                         };
84833                     })
84834                     .sort(function(a, b) {
84835                         return a.dist - b.dist;
84836                     });
84837
84838                 return utilArrayUniqBy(streets, 'value');
84839
84840                 function isAddressable(d) {
84841                     return d.tags.highway && d.tags.name && d.type === 'way';
84842                 }
84843             }
84844
84845
84846             function getNearCities() {
84847                 var extent = combinedEntityExtent();
84848                 var l = extent.center();
84849                 var box = geoExtent(l).padByMeters(200);
84850
84851                 var cities = context.history().intersects(box)
84852                     .filter(isAddressable)
84853                     .map(function(d) {
84854                         return {
84855                             title: d.tags['addr:city'] || d.tags.name,
84856                             value: d.tags['addr:city'] || d.tags.name,
84857                             dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
84858                         };
84859                     })
84860                     .sort(function(a, b) {
84861                         return a.dist - b.dist;
84862                     });
84863
84864                 return utilArrayUniqBy(cities, 'value');
84865
84866
84867                 function isAddressable(d) {
84868                     if (d.tags.name) {
84869                         if (d.tags.admin_level === '8' && d.tags.boundary === 'administrative')
84870                             return true;
84871                         if (d.tags.border_type === 'city')
84872                             return true;
84873                         if (d.tags.place === 'city' || d.tags.place === 'town' || d.tags.place === 'village')
84874                             return true;
84875                     }
84876
84877                     if (d.tags['addr:city'])
84878                         return true;
84879
84880                     return false;
84881                 }
84882             }
84883
84884             function getNearValues(key) {
84885                 var extent = combinedEntityExtent();
84886                 var l = extent.center();
84887                 var box = geoExtent(l).padByMeters(200);
84888
84889                 var results = context.history().intersects(box)
84890                     .filter(function hasTag(d) { return _entityIDs.indexOf(d.id) === -1 && d.tags[key]; })
84891                     .map(function(d) {
84892                         return {
84893                             title: d.tags[key],
84894                             value: d.tags[key],
84895                             dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
84896                         };
84897                     })
84898                     .sort(function(a, b) {
84899                         return a.dist - b.dist;
84900                     });
84901
84902                 return utilArrayUniqBy(results, 'value');
84903             }
84904
84905
84906             function updateForCountryCode() {
84907
84908                 if (!_countryCode) return;
84909
84910                 var addressFormat;
84911                 for (var i = 0; i < _addressFormats.length; i++) {
84912                     var format = _addressFormats[i];
84913                     if (!format.countryCodes) {
84914                         addressFormat = format;   // choose the default format, keep going
84915                     } else if (format.countryCodes.indexOf(_countryCode) !== -1) {
84916                         addressFormat = format;   // choose the country format, stop here
84917                         break;
84918                     }
84919                 }
84920
84921                 var dropdowns = addressFormat.dropdowns || [
84922                     'city', 'county', 'country', 'district', 'hamlet',
84923                     'neighbourhood', 'place', 'postcode', 'province',
84924                     'quarter', 'state', 'street', 'subdistrict', 'suburb'
84925                 ];
84926
84927                 var widths = addressFormat.widths || {
84928                     housenumber: 1/3, street: 2/3,
84929                     city: 2/3, state: 1/4, postcode: 1/3
84930                 };
84931
84932                 function row(r) {
84933                     // Normalize widths.
84934                     var total = r.reduce(function(sum, key) {
84935                         return sum + (widths[key] || 0.5);
84936                     }, 0);
84937
84938                     return r.map(function(key) {
84939                         return {
84940                             id: key,
84941                             width: (widths[key] || 0.5) / total
84942                         };
84943                     });
84944                 }
84945
84946                 var rows = _wrap.selectAll('.addr-row')
84947                     .data(addressFormat.format, function(d) {
84948                         return d.toString();
84949                     });
84950
84951                 rows.exit()
84952                     .remove();
84953
84954                 rows
84955                     .enter()
84956                     .append('div')
84957                     .attr('class', 'addr-row')
84958                     .selectAll('input')
84959                     .data(row)
84960                     .enter()
84961                     .append('input')
84962                     .property('type', 'text')
84963                     .call(updatePlaceholder)
84964                     .attr('class', function (d) { return 'addr-' + d.id; })
84965                     .call(utilNoAuto)
84966                     .each(addDropdown)
84967                     .style('width', function (d) { return d.width * 100 + '%'; });
84968
84969
84970                 function addDropdown(d) {
84971                     if (dropdowns.indexOf(d.id) === -1) return;  // not a dropdown
84972
84973                     var nearValues = (d.id === 'street') ? getNearStreets
84974                         : (d.id === 'city') ? getNearCities
84975                         : getNearValues;
84976
84977                     select(this)
84978                         .call(uiCombobox(context, 'address-' + d.id)
84979                             .minItems(1)
84980                             .caseSensitive(true)
84981                             .fetcher(function(value, callback) {
84982                                 callback(nearValues('addr:' + d.id));
84983                             })
84984                         );
84985                 }
84986
84987                 _wrap.selectAll('input')
84988                     .on('blur', change())
84989                     .on('change', change());
84990
84991                 _wrap.selectAll('input:not(.combobox-input)')
84992                     .on('input', change(true));
84993
84994                 if (_tags) updateTags(_tags);
84995             }
84996
84997
84998             function address(selection) {
84999                 _selection = selection;
85000
85001                 _wrap = selection.selectAll('.form-field-input-wrap')
85002                     .data([0]);
85003
85004                 _wrap = _wrap.enter()
85005                     .append('div')
85006                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85007                     .merge(_wrap);
85008
85009                 var extent = combinedEntityExtent();
85010
85011                 if (extent) {
85012                     var countryCode;
85013                     if (context.inIntro()) {
85014                         // localize the address format for the walkthrough
85015                         countryCode = _t('intro.graph.countrycode');
85016                     } else {
85017                         var center = extent.center();
85018                         countryCode = iso1A2Code(center);
85019                     }
85020                     if (countryCode) {
85021                         _countryCode = countryCode.toLowerCase();
85022                         updateForCountryCode();
85023                     }
85024                 }
85025             }
85026
85027
85028             function change(onInput) {
85029                 return function() {
85030                     var tags = {};
85031
85032                     _wrap.selectAll('input')
85033                         .each(function (subfield) {
85034                             var key = field.key + ':' + subfield.id;
85035
85036                             var value = this.value;
85037                             if (!onInput) value = context.cleanTagValue(value);
85038
85039                             // don't override multiple values with blank string
85040                             if (Array.isArray(_tags[key]) && !value) return;
85041
85042                             tags[key] = value || undefined;
85043                         });
85044
85045                     dispatch$1.call('change', this, tags, onInput);
85046                 };
85047             }
85048
85049             function updatePlaceholder(inputSelection) {
85050                 return inputSelection.attr('placeholder', function(subfield) {
85051                     if (_tags && Array.isArray(_tags[field.key + ':' + subfield.id])) {
85052                         return _t('inspector.multiple_values');
85053                     }
85054                     if (_countryCode) {
85055                         var localkey = subfield.id + '!' + _countryCode;
85056                         var tkey = addrField.strings.placeholders[localkey] ? localkey : subfield.id;
85057                         return addrField.t('placeholders.' + tkey);
85058                     }
85059                 });
85060             }
85061
85062
85063             function updateTags(tags) {
85064                 utilGetSetValue(_wrap.selectAll('input'), function (subfield) {
85065                         var val = tags[field.key + ':' + subfield.id];
85066                         return typeof val === 'string' ? val : '';
85067                     })
85068                     .attr('title', function(subfield) {
85069                         var val = tags[field.key + ':' + subfield.id];
85070                         return val && Array.isArray(val) && val.filter(Boolean).join('\n');
85071                     })
85072                     .classed('mixed', function(subfield) {
85073                         return Array.isArray(tags[field.key + ':' + subfield.id]);
85074                     })
85075                     .call(updatePlaceholder);
85076             }
85077
85078
85079             function combinedEntityExtent() {
85080                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
85081             }
85082
85083
85084             address.entityIDs = function(val) {
85085                 if (!arguments.length) return _entityIDs;
85086                 _entityIDs = val;
85087                 return address;
85088             };
85089
85090
85091             address.tags = function(tags) {
85092                 _tags = tags;
85093                 updateTags(tags);
85094             };
85095
85096
85097             address.focus = function() {
85098                 var node = _wrap.selectAll('input').node();
85099                 if (node) node.focus();
85100             };
85101
85102
85103             return utilRebind(address, dispatch$1, 'on');
85104         }
85105
85106         function uiFieldCycleway(field, context) {
85107             var dispatch$1 = dispatch('change');
85108             var items = select(null);
85109             var wrap = select(null);
85110             var _tags;
85111
85112             function cycleway(selection) {
85113
85114                 function stripcolon(s) {
85115                     return s.replace(':', '');
85116                 }
85117
85118
85119                 wrap = selection.selectAll('.form-field-input-wrap')
85120                     .data([0]);
85121
85122                 wrap = wrap.enter()
85123                     .append('div')
85124                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85125                     .merge(wrap);
85126
85127
85128                 var div = wrap.selectAll('ul')
85129                     .data([0]);
85130
85131                 div = div.enter()
85132                     .append('ul')
85133                     .attr('class', 'rows')
85134                     .merge(div);
85135
85136                 var keys = ['cycleway:left', 'cycleway:right'];
85137
85138                 items = div.selectAll('li')
85139                     .data(keys);
85140
85141                 var enter = items.enter()
85142                     .append('li')
85143                     .attr('class', function(d) { return 'labeled-input preset-cycleway-' + stripcolon(d); });
85144
85145                 enter
85146                     .append('span')
85147                     .attr('class', 'label preset-label-cycleway')
85148                     .attr('for', function(d) { return 'preset-input-cycleway-' + stripcolon(d); })
85149                     .text(function(d) { return field.t('types.' + d); });
85150
85151                 enter
85152                     .append('div')
85153                     .attr('class', 'preset-input-cycleway-wrap')
85154                     .append('input')
85155                     .attr('type', 'text')
85156                     .attr('class', function(d) { return 'preset-input-cycleway preset-input-' + stripcolon(d); })
85157                     .call(utilNoAuto)
85158                     .each(function(d) {
85159                         select(this)
85160                             .call(uiCombobox(context, 'cycleway-' + stripcolon(d))
85161                                 .data(cycleway.options(d))
85162                             );
85163                     });
85164
85165                 items = items.merge(enter);
85166
85167                 // Update
85168                 wrap.selectAll('.preset-input-cycleway')
85169                     .on('change', change)
85170                     .on('blur', change);
85171             }
85172
85173
85174             function change(key) {
85175
85176                 var newValue = context.cleanTagValue(utilGetSetValue(select(this)));
85177
85178                 // don't override multiple values with blank string
85179                 if (!newValue && (Array.isArray(_tags.cycleway) || Array.isArray(_tags[key]))) return;
85180
85181                 if (newValue === 'none' || newValue === '') { newValue = undefined; }
85182
85183                 var otherKey = key === 'cycleway:left' ? 'cycleway:right' : 'cycleway:left';
85184                 var otherValue = typeof _tags.cycleway === 'string' ? _tags.cycleway : _tags[otherKey];
85185                 if (otherValue && Array.isArray(otherValue)) {
85186                     // we must always have an explicit value for comparison
85187                     otherValue = otherValue[0];
85188                 }
85189                 if (otherValue === 'none' || otherValue === '') { otherValue = undefined; }
85190
85191                 var tag = {};
85192
85193                 // If the left and right tags match, use the cycleway tag to tag both
85194                 // sides the same way
85195                 if (newValue === otherValue) {
85196                     tag = {
85197                         cycleway: newValue,
85198                         'cycleway:left': undefined,
85199                         'cycleway:right': undefined
85200                     };
85201                 } else {
85202                     // Always set both left and right as changing one can affect the other
85203                     tag = {
85204                         cycleway: undefined
85205                     };
85206                     tag[key] = newValue;
85207                     tag[otherKey] = otherValue;
85208                 }
85209
85210                 dispatch$1.call('change', this, tag);
85211             }
85212
85213
85214             cycleway.options = function() {
85215                 return Object.keys(field.strings.options).map(function(option) {
85216                     return {
85217                         title: field.t('options.' + option + '.description'),
85218                         value: option
85219                     };
85220                 });
85221             };
85222
85223
85224             cycleway.tags = function(tags) {
85225                 _tags = tags;
85226
85227                 // If cycleway is set, use that instead of individual values
85228                 var commonValue = typeof tags.cycleway === 'string' && tags.cycleway;
85229
85230                 utilGetSetValue(items.selectAll('.preset-input-cycleway'), function(d) {
85231                         if (commonValue) return commonValue;
85232                         return !tags.cycleway && typeof tags[d] === 'string' ? tags[d] : '';
85233                     })
85234                     .attr('title', function(d) {
85235                         if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85236                             var vals = [];
85237                             if (Array.isArray(tags.cycleway)) {
85238                                 vals = vals.concat(tags.cycleway);
85239                             }
85240                             if (Array.isArray(tags[d])) {
85241                                 vals = vals.concat(tags[d]);
85242                             }
85243                             return vals.filter(Boolean).join('\n');
85244                         }
85245                         return null;
85246                     })
85247                     .attr('placeholder', function(d) {
85248                         if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85249                             return _t('inspector.multiple_values');
85250                         }
85251                         return field.placeholder();
85252                     })
85253                     .classed('mixed', function(d) {
85254                         return Array.isArray(tags.cycleway) || Array.isArray(tags[d]);
85255                     });
85256             };
85257
85258
85259             cycleway.focus = function() {
85260                 var node = wrap.selectAll('input').node();
85261                 if (node) node.focus();
85262             };
85263
85264
85265             return utilRebind(cycleway, dispatch$1, 'on');
85266         }
85267
85268         function uiFieldLanes(field, context) {
85269             var dispatch$1 = dispatch('change');
85270             var LANE_WIDTH = 40;
85271             var LANE_HEIGHT = 200;
85272             var _entityIDs = [];
85273
85274             function lanes(selection) {
85275                 var lanesData = context.entity(_entityIDs[0]).lanes();
85276
85277                 if (!context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
85278                     selection.call(lanes.off);
85279                     return;
85280                 }
85281
85282                 var wrap = selection.selectAll('.form-field-input-wrap')
85283                     .data([0]);
85284
85285                 wrap = wrap.enter()
85286                     .append('div')
85287                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85288                     .merge(wrap);
85289
85290                 var surface =  wrap.selectAll('.surface')
85291                     .data([0]);
85292
85293                 var d = utilGetDimensions(wrap);
85294                 var freeSpace = d[0] - lanesData.lanes.length * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5;
85295
85296                 surface = surface.enter()
85297                     .append('svg')
85298                     .attr('width', d[0])
85299                     .attr('height', 300)
85300                     .attr('class', 'surface')
85301                     .merge(surface);
85302
85303
85304                 var lanesSelection = surface.selectAll('.lanes')
85305                     .data([0]);
85306
85307                 lanesSelection = lanesSelection.enter()
85308                     .append('g')
85309                     .attr('class', 'lanes')
85310                     .merge(lanesSelection);
85311
85312                 lanesSelection
85313                     .attr('transform', function () {
85314                         return 'translate(' + (freeSpace / 2) + ', 0)';
85315                     });
85316
85317
85318                 var lane = lanesSelection.selectAll('.lane')
85319                    .data(lanesData.lanes);
85320
85321                 lane.exit()
85322                     .remove();
85323
85324                 var enter = lane.enter()
85325                     .append('g')
85326                     .attr('class', 'lane');
85327
85328                 enter
85329                     .append('g')
85330                     .append('rect')
85331                     .attr('y', 50)
85332                     .attr('width', LANE_WIDTH)
85333                     .attr('height', LANE_HEIGHT);
85334
85335                 enter
85336                     .append('g')
85337                     .attr('class', 'forward')
85338                     .append('text')
85339                     .attr('y', 40)
85340                     .attr('x', 14)
85341                     .text('▲');
85342
85343                 enter
85344                     .append('g')
85345                     .attr('class', 'bothways')
85346                     .append('text')
85347                     .attr('y', 40)
85348                     .attr('x', 14)
85349                     .text('▲▼');
85350
85351                 enter
85352                     .append('g')
85353                     .attr('class', 'backward')
85354                     .append('text')
85355                     .attr('y', 40)
85356                     .attr('x', 14)
85357                     .text('▼');
85358
85359
85360                 lane = lane
85361                     .merge(enter);
85362
85363                 lane
85364                     .attr('transform', function(d) {
85365                         return 'translate(' + (LANE_WIDTH * d.index * 1.5) + ', 0)';
85366                     });
85367
85368                 lane.select('.forward')
85369                     .style('visibility', function(d) {
85370                         return d.direction === 'forward' ? 'visible' : 'hidden';
85371                     });
85372
85373                 lane.select('.bothways')
85374                     .style('visibility', function(d) {
85375                         return d.direction === 'bothways' ? 'visible' : 'hidden';
85376                     });
85377
85378                 lane.select('.backward')
85379                     .style('visibility', function(d) {
85380                         return d.direction === 'backward' ? 'visible' : 'hidden';
85381                     });
85382             }
85383
85384
85385             lanes.entityIDs = function(val) {
85386                 _entityIDs = val;
85387             };
85388
85389             lanes.tags = function() {};
85390             lanes.focus = function() {};
85391             lanes.off = function() {};
85392
85393             return utilRebind(lanes, dispatch$1, 'on');
85394         }
85395
85396         uiFieldLanes.supportsMultiselection = false;
85397
85398         var _languagesArray = [];
85399
85400
85401         function uiFieldLocalized(field, context) {
85402             var dispatch$1 = dispatch('change', 'input');
85403             var wikipedia = services.wikipedia;
85404             var input = select(null);
85405             var localizedInputs = select(null);
85406             var _countryCode;
85407             var _tags;
85408
85409
85410             // A concern here in switching to async data means that _languagesArray will not
85411             // be available the first time through, so things like the fetchers and
85412             // the language() function will not work immediately.
85413             _mainFileFetcher.get('languages')
85414                 .then(loadLanguagesArray)
85415                 .catch(function() { /* ignore */ });
85416
85417             var _territoryLanguages = {};
85418             _mainFileFetcher.get('territory_languages')
85419                 .then(function(d) { _territoryLanguages = d; })
85420                 .catch(function() { /* ignore */ });
85421
85422
85423             var allSuggestions = _mainPresetIndex.collection.filter(function(p) {
85424                 return p.suggestion === true;
85425             });
85426
85427             // reuse these combos
85428             var langCombo = uiCombobox(context, 'localized-lang')
85429                 .fetcher(fetchLanguages)
85430                 .minItems(0);
85431
85432             var brandCombo = uiCombobox(context, 'localized-brand')
85433                 .canAutocomplete(false)
85434                 .minItems(1);
85435
85436             var _selection = select(null);
85437             var _multilingual = [];
85438             var _buttonTip = uiTooltip()
85439                 .title(_t('translate.translate'))
85440                 .placement('left');
85441             var _wikiTitles;
85442             var _entityIDs = [];
85443
85444
85445             function loadLanguagesArray(dataLanguages) {
85446                 if (_languagesArray.length !== 0) return;
85447
85448                 // some conversion is needed to ensure correct OSM tags are used
85449                 var replacements = {
85450                     sr: 'sr-Cyrl',      // in OSM, `sr` implies Cyrillic
85451                     'sr-Cyrl': false    // `sr-Cyrl` isn't used in OSM
85452                 };
85453
85454                 for (var code in dataLanguages) {
85455                     if (replacements[code] === false) continue;
85456                     var metaCode = code;
85457                     if (replacements[code]) metaCode = replacements[code];
85458
85459                     _languagesArray.push({
85460                         localName: _mainLocalizer.languageName(metaCode, { localOnly: true }),
85461                         nativeName: dataLanguages[metaCode].nativeName,
85462                         code: code,
85463                         label: _mainLocalizer.languageName(metaCode)
85464                     });
85465                 }
85466             }
85467
85468
85469             function calcLocked() {
85470
85471                 // only lock the Name field
85472                 var isLocked = field.id === 'name' &&
85473                     _entityIDs.length &&
85474                     // lock the field if any feature needs it
85475                     _entityIDs.some(function(entityID) {
85476
85477                         var entity = context.graph().hasEntity(entityID);
85478                         if (!entity) return false;
85479
85480                         var original = context.graph().base().entities[_entityIDs[0]];
85481                         var hasOriginalName = original && entity.tags.name && entity.tags.name === original.tags.name;
85482                         // if the name was already edited manually then allow further editing
85483                         if (!hasOriginalName) return false;
85484
85485                         // features linked to Wikidata are likely important and should be protected
85486                         if (entity.tags.wikidata) return true;
85487
85488                         // assume the name has already been confirmed if its source has been researched
85489                         if (entity.tags['name:etymology:wikidata']) return true;
85490
85491                         var preset = _mainPresetIndex.match(entity, context.graph());
85492                         var isSuggestion = preset && preset.suggestion;
85493                         var showsBrand = preset && preset.originalFields.filter(function(d) {
85494                             return d.id === 'brand';
85495                         }).length;
85496                         // protect standardized brand names
85497                         return isSuggestion && !showsBrand;
85498                     });
85499
85500                 field.locked(isLocked);
85501             }
85502
85503
85504             // update _multilingual, maintaining the existing order
85505             function calcMultilingual(tags) {
85506                 var existingLangsOrdered = _multilingual.map(function(item) {
85507                     return item.lang;
85508                 });
85509                 var existingLangs = new Set(existingLangsOrdered.filter(Boolean));
85510
85511                 for (var k in tags) {
85512                     var m = k.match(/^(.*):([a-zA-Z_-]+)$/);
85513                     if (m && m[1] === field.key && m[2]) {
85514                         var item = { lang: m[2], value: tags[k] };
85515                         if (existingLangs.has(item.lang)) {
85516                             // update the value
85517                             _multilingual[existingLangsOrdered.indexOf(item.lang)].value = item.value;
85518                             existingLangs.delete(item.lang);
85519                         } else {
85520                             _multilingual.push(item);
85521                         }
85522                     }
85523                 }
85524
85525                 _multilingual = _multilingual.filter(function(item) {
85526                     return !item.lang || !existingLangs.has(item.lang);
85527                 });
85528             }
85529
85530
85531             function localized(selection) {
85532                 _selection = selection;
85533                 calcLocked();
85534                 var isLocked = field.locked();
85535                 var singularEntity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85536                 var preset = singularEntity && _mainPresetIndex.match(singularEntity, context.graph());
85537
85538                 var wrap = selection.selectAll('.form-field-input-wrap')
85539                     .data([0]);
85540
85541                 // enter/update
85542                 wrap = wrap.enter()
85543                     .append('div')
85544                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85545                     .merge(wrap);
85546
85547                 input = wrap.selectAll('.localized-main')
85548                     .data([0]);
85549
85550                 // enter/update
85551                 input = input.enter()
85552                     .append('input')
85553                     .attr('type', 'text')
85554                     .attr('id', field.domId)
85555                     .attr('class', 'localized-main')
85556                     .call(utilNoAuto)
85557                     .merge(input);
85558
85559                 if (preset && field.id === 'name') {
85560                     var pTag = preset.id.split('/', 2);
85561                     var pKey = pTag[0];
85562                     var pValue = pTag[1];
85563
85564                     if (!preset.suggestion) {
85565                         // Not a suggestion preset - Add a suggestions dropdown if it makes sense to.
85566                         // This code attempts to determine if the matched preset is the
85567                         // kind of preset that even can benefit from name suggestions..
85568                         // - true = shops, cafes, hotels, etc. (also generic and fallback presets)
85569                         // - false = churches, parks, hospitals, etc. (things not in the index)
85570                         var isFallback = preset.isFallback();
85571                         var goodSuggestions = allSuggestions.filter(function(s) {
85572                             if (isFallback) return true;
85573                             var sTag = s.id.split('/', 2);
85574                             var sKey = sTag[0];
85575                             var sValue = sTag[1];
85576                             return pKey === sKey && (!pValue || pValue === sValue);
85577                         });
85578
85579                         // Show the suggestions.. If the user picks one, change the tags..
85580                         if (allSuggestions.length && goodSuggestions.length) {
85581                             input
85582                                 .on('blur.localized', checkBrandOnBlur)
85583                                 .call(brandCombo
85584                                     .fetcher(fetchBrandNames(preset, allSuggestions))
85585                                     .on('accept', acceptBrand)
85586                                     .on('cancel', cancelBrand)
85587                                 );
85588                         }
85589                     }
85590                 }
85591
85592                 input
85593                     .classed('disabled', !!isLocked)
85594                     .attr('readonly', isLocked || null)
85595                     .on('input', change(true))
85596                     .on('blur', change())
85597                     .on('change', change());
85598
85599
85600                 var translateButton = wrap.selectAll('.localized-add')
85601                     .data([0]);
85602
85603                 translateButton = translateButton.enter()
85604                     .append('button')
85605                     .attr('class', 'localized-add form-field-button')
85606                     .attr('tabindex', -1)
85607                     .call(svgIcon('#iD-icon-plus'))
85608                     .merge(translateButton);
85609
85610                 translateButton
85611                     .classed('disabled', !!isLocked)
85612                     .call(isLocked ? _buttonTip.destroy : _buttonTip)
85613                     .on('click', addNew);
85614
85615
85616                 if (_tags && !_multilingual.length) {
85617                     calcMultilingual(_tags);
85618                 }
85619
85620                 localizedInputs = selection.selectAll('.localized-multilingual')
85621                     .data([0]);
85622
85623                 localizedInputs = localizedInputs.enter()
85624                     .append('div')
85625                     .attr('class', 'localized-multilingual')
85626                     .merge(localizedInputs);
85627
85628                 localizedInputs
85629                     .call(renderMultilingual);
85630
85631                 localizedInputs.selectAll('button, input')
85632                     .classed('disabled', !!isLocked)
85633                     .attr('readonly', isLocked || null);
85634
85635
85636
85637                 // We are not guaranteed to get an `accept` or `cancel` when blurring the field.
85638                 // (This can happen if the user actives the combo, arrows down, and then clicks off to blur)
85639                 // So compare the current field value against the suggestions one last time.
85640                 function checkBrandOnBlur() {
85641                     var latest = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85642                     if (!latest) return;   // deleting the entity blurred the field?
85643
85644                     var preset = _mainPresetIndex.match(latest, context.graph());
85645                     if (preset && preset.suggestion) return;   // already accepted
85646
85647                     // note: here we are testing against "decorated" names, i.e. 'Starbucks – Cafe'
85648                     var name = utilGetSetValue(input).trim();
85649                     var matched = allSuggestions.filter(function(s) { return name === s.name(); });
85650
85651                     if (matched.length === 1) {
85652                         acceptBrand({ suggestion: matched[0] });
85653                     } else {
85654                         cancelBrand();
85655                     }
85656                 }
85657
85658
85659                 function acceptBrand(d) {
85660
85661                     var entity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85662
85663                     if (!d || !entity) {
85664                         cancelBrand();
85665                         return;
85666                     }
85667
85668                     var tags = entity.tags;
85669                     var geometry = entity.geometry(context.graph());
85670                     var removed = preset.unsetTags(tags, geometry);
85671                     for (var k in tags) {
85672                         tags[k] = removed[k];  // set removed tags to `undefined`
85673                     }
85674                     tags = d.suggestion.setTags(tags, geometry);
85675                     utilGetSetValue(input, tags.name);
85676                     dispatch$1.call('change', this, tags);
85677                 }
85678
85679
85680                 // user hit escape, clean whatever preset name appears after the last ' – '
85681                 function cancelBrand() {
85682                     var name = utilGetSetValue(input);
85683                     var clean = cleanName(name);
85684                     if (clean !== name) {
85685                         utilGetSetValue(input, clean);
85686                         dispatch$1.call('change', this, { name: clean });
85687                     }
85688                 }
85689
85690                 // Remove whatever is after the last ' – '
85691                 // NOTE: split/join on en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc)
85692                 function cleanName(name) {
85693                     var parts = name.split(' – ');
85694                     if (parts.length > 1) {
85695                         parts.pop();
85696                         name = parts.join(' – ');
85697                     }
85698                     return name;
85699                 }
85700
85701
85702                 function fetchBrandNames(preset, suggestions) {
85703                     var pTag = preset.id.split('/', 2);
85704                     var pKey = pTag[0];
85705                     var pValue = pTag[1];
85706
85707                     return function(value, callback) {
85708                         var results = [];
85709                         if (value && value.length > 2) {
85710                             for (var i = 0; i < suggestions.length; i++) {
85711                                 var s = suggestions[i];
85712
85713                                 // don't suggest brands from incompatible countries
85714                                 if (_countryCode && s.countryCodes &&
85715                                     s.countryCodes.indexOf(_countryCode) === -1) continue;
85716
85717                                 var sTag = s.id.split('/', 2);
85718                                 var sKey = sTag[0];
85719                                 var sValue = sTag[1];
85720                                 var name = s.name();
85721                                 var dist = utilEditDistance(value, name.substring(0, value.length));
85722                                 var matchesPreset = (pKey === sKey && (!pValue || pValue === sValue));
85723
85724                                 if (dist < 1 || (matchesPreset && dist < 3)) {
85725                                     var obj = {
85726                                         title: name,
85727                                         value: name,
85728                                         suggestion: s,
85729                                         dist: dist + (matchesPreset ? 0 : 1)  // penalize if not matched preset
85730                                     };
85731                                     results.push(obj);
85732                                 }
85733                             }
85734                             results.sort(function(a, b) { return a.dist - b.dist; });
85735                         }
85736                         results = results.slice(0, 10);
85737                         callback(results);
85738                     };
85739                 }
85740
85741
85742                 function addNew() {
85743                     event.preventDefault();
85744                     if (field.locked()) return;
85745
85746                     var defaultLang = _mainLocalizer.languageCode().toLowerCase();
85747                     var langExists = _multilingual.find(function(datum) { return datum.lang === defaultLang; });
85748                     var isLangEn = defaultLang.indexOf('en') > -1;
85749                     if (isLangEn || langExists) {
85750                         defaultLang = '';
85751                         langExists = _multilingual.find(function(datum) { return datum.lang === defaultLang; });
85752                     }
85753
85754                     if (!langExists) {
85755                         // prepend the value so it appears at the top
85756                         _multilingual.unshift({ lang: defaultLang, value: '' });
85757
85758                         localizedInputs
85759                             .call(renderMultilingual);
85760                     }
85761                 }
85762
85763
85764                 function change(onInput) {
85765                     return function() {
85766                         if (field.locked()) {
85767                             event.preventDefault();
85768                             return;
85769                         }
85770
85771                         var val = utilGetSetValue(select(this));
85772                         if (!onInput) val = context.cleanTagValue(val);
85773
85774                         // don't override multiple values with blank string
85775                         if (!val && Array.isArray(_tags[field.key])) return;
85776
85777                         var t = {};
85778
85779                         t[field.key] = val || undefined;
85780                         dispatch$1.call('change', this, t, onInput);
85781                     };
85782                 }
85783             }
85784
85785
85786             function key(lang) {
85787                 return field.key + ':' + lang;
85788             }
85789
85790
85791             function changeLang(d) {
85792                 var tags = {};
85793
85794                 // make sure unrecognized suffixes are lowercase - #7156
85795                 var lang = utilGetSetValue(select(this)).toLowerCase();
85796
85797                 var language = _languagesArray.find(function(d) {
85798                     return d.label.toLowerCase() === lang ||
85799                         (d.localName && d.localName.toLowerCase() === lang) ||
85800                         (d.nativeName && d.nativeName.toLowerCase() === lang);
85801                 });
85802                 if (language) lang = language.code;
85803
85804                 if (d.lang && d.lang !== lang) {
85805                     tags[key(d.lang)] = undefined;
85806                 }
85807
85808                 var newKey = lang && context.cleanTagKey(key(lang));
85809
85810                 var value = utilGetSetValue(select(this.parentNode).selectAll('.localized-value'));
85811
85812                 if (newKey && value) {
85813                     tags[newKey] = value;
85814                 } else if (newKey && _wikiTitles && _wikiTitles[d.lang]) {
85815                     tags[newKey] = _wikiTitles[d.lang];
85816                 }
85817
85818                 d.lang = lang;
85819                 dispatch$1.call('change', this, tags);
85820             }
85821
85822
85823             function changeValue(d) {
85824                 if (!d.lang) return;
85825                 var value = context.cleanTagValue(utilGetSetValue(select(this))) || undefined;
85826
85827                 // don't override multiple values with blank string
85828                 if (!value && Array.isArray(d.value)) return;
85829
85830                 var t = {};
85831                 t[key(d.lang)] = value;
85832                 d.value = value;
85833                 dispatch$1.call('change', this, t);
85834             }
85835
85836
85837             function fetchLanguages(value, cb) {
85838                 var v = value.toLowerCase();
85839
85840                 // show the user's language first
85841                 var langCodes = [_mainLocalizer.localeCode(), _mainLocalizer.languageCode()];
85842
85843                 if (_countryCode && _territoryLanguages[_countryCode]) {
85844                     langCodes = langCodes.concat(_territoryLanguages[_countryCode]);
85845                 }
85846
85847                 var langItems = [];
85848                 langCodes.forEach(function(code) {
85849                     var langItem = _languagesArray.find(function(item) {
85850                         return item.code === code;
85851                     });
85852                     if (langItem) langItems.push(langItem);
85853                 });
85854                 langItems = utilArrayUniq(langItems.concat(_languagesArray));
85855
85856                 cb(langItems.filter(function(d) {
85857                     return d.label.toLowerCase().indexOf(v) >= 0 ||
85858                         (d.localName && d.localName.toLowerCase().indexOf(v) >= 0) ||
85859                         (d.nativeName && d.nativeName.toLowerCase().indexOf(v) >= 0) ||
85860                         d.code.toLowerCase().indexOf(v) >= 0;
85861                 }).map(function(d) {
85862                     return { value: d.label };
85863                 }));
85864             }
85865
85866
85867             function renderMultilingual(selection) {
85868                 var entries = selection.selectAll('div.entry')
85869                     .data(_multilingual, function(d) { return d.lang; });
85870
85871                 entries.exit()
85872                     .style('top', '0')
85873                     .style('max-height', '240px')
85874                     .transition()
85875                     .duration(200)
85876                     .style('opacity', '0')
85877                     .style('max-height', '0px')
85878                     .remove();
85879
85880                 var entriesEnter = entries.enter()
85881                     .append('div')
85882                     .attr('class', 'entry')
85883                     .each(function(_, index) {
85884                         var wrap = select(this);
85885
85886                         var domId = utilUniqueDomId(index);
85887
85888                         var label = wrap
85889                             .append('label')
85890                             .attr('class', 'field-label')
85891                             .attr('for', domId);
85892
85893                         var text = label
85894                             .append('span')
85895                             .attr('class', 'label-text');
85896
85897                         text
85898                             .append('span')
85899                             .attr('class', 'label-textvalue')
85900                             .text(_t('translate.localized_translation_label'));
85901
85902                         text
85903                             .append('span')
85904                             .attr('class', 'label-textannotation');
85905
85906                         label
85907                             .append('button')
85908                             .attr('class', 'remove-icon-multilingual')
85909                             .on('click', function(d, index) {
85910                                 if (field.locked()) return;
85911                                 event.preventDefault();
85912
85913                                 if (!d.lang || !d.value) {
85914                                     _multilingual.splice(index, 1);
85915                                     renderMultilingual(selection);
85916                                 } else {
85917                                     // remove from entity tags
85918                                     var t = {};
85919                                     t[key(d.lang)] = undefined;
85920                                     dispatch$1.call('change', this, t);
85921                                 }
85922
85923                             })
85924                             .call(svgIcon('#iD-operation-delete'));
85925
85926                         wrap
85927                             .append('input')
85928                             .attr('class', 'localized-lang')
85929                             .attr('id', domId)
85930                             .attr('type', 'text')
85931                             .attr('placeholder', _t('translate.localized_translation_language'))
85932                             .on('blur', changeLang)
85933                             .on('change', changeLang)
85934                             .call(langCombo);
85935
85936                         wrap
85937                             .append('input')
85938                             .attr('type', 'text')
85939                             .attr('class', 'localized-value')
85940                             .on('blur', changeValue)
85941                             .on('change', changeValue);
85942                     });
85943
85944                 entriesEnter
85945                     .style('margin-top', '0px')
85946                     .style('max-height', '0px')
85947                     .style('opacity', '0')
85948                     .transition()
85949                     .duration(200)
85950                     .style('margin-top', '10px')
85951                     .style('max-height', '240px')
85952                     .style('opacity', '1')
85953                     .on('end', function() {
85954                         select(this)
85955                             .style('max-height', '')
85956                             .style('overflow', 'visible');
85957                     });
85958
85959                 entries = entries.merge(entriesEnter);
85960
85961                 entries.order();
85962
85963                 entries.classed('present', function(d) {
85964                     return d.lang && d.value;
85965                 });
85966
85967                 utilGetSetValue(entries.select('.localized-lang'), function(d) {
85968                     return _mainLocalizer.languageName(d.lang);
85969                 });
85970
85971                 utilGetSetValue(entries.select('.localized-value'), function(d) {
85972                         return typeof d.value === 'string' ? d.value : '';
85973                     })
85974                     .attr('title', function(d) {
85975                         return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : null;
85976                     })
85977                     .attr('placeholder', function(d) {
85978                         return Array.isArray(d.value) ? _t('inspector.multiple_values') : _t('translate.localized_translation_name');
85979                     })
85980                     .classed('mixed', function(d) {
85981                         return Array.isArray(d.value);
85982                     });
85983             }
85984
85985
85986             localized.tags = function(tags) {
85987                 _tags = tags;
85988
85989                 // Fetch translations from wikipedia
85990                 if (typeof tags.wikipedia === 'string' && !_wikiTitles) {
85991                     _wikiTitles = {};
85992                     var wm = tags.wikipedia.match(/([^:]+):(.+)/);
85993                     if (wm && wm[0] && wm[1]) {
85994                         wikipedia.translations(wm[1], wm[2], function(err, d) {
85995                             if (err || !d) return;
85996                             _wikiTitles = d;
85997                         });
85998                     }
85999                 }
86000
86001                 var isMixed = Array.isArray(tags[field.key]);
86002
86003                 utilGetSetValue(input, typeof tags[field.key] === 'string' ? tags[field.key] : '')
86004                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
86005                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder())
86006                     .classed('mixed', isMixed);
86007
86008                 calcMultilingual(tags);
86009
86010                 _selection
86011                     .call(localized);
86012             };
86013
86014
86015             localized.focus = function() {
86016                 input.node().focus();
86017             };
86018
86019
86020             localized.entityIDs = function(val) {
86021                 if (!arguments.length) return _entityIDs;
86022                 _entityIDs = val;
86023                 _multilingual = [];
86024                 loadCountryCode();
86025                 return localized;
86026             };
86027
86028             function loadCountryCode() {
86029                 var extent = combinedEntityExtent();
86030                 var countryCode = extent && iso1A2Code(extent.center());
86031                 _countryCode = countryCode && countryCode.toLowerCase();
86032             }
86033
86034             function combinedEntityExtent() {
86035                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86036             }
86037
86038             return utilRebind(localized, dispatch$1, 'on');
86039         }
86040
86041         function uiFieldMaxspeed(field, context) {
86042             var dispatch$1 = dispatch('change');
86043             var unitInput = select(null);
86044             var input = select(null);
86045             var _entityIDs = [];
86046             var _tags;
86047             var _isImperial;
86048
86049             var speedCombo = uiCombobox(context, 'maxspeed');
86050             var unitCombo = uiCombobox(context, 'maxspeed-unit')
86051                     .data(['km/h', 'mph'].map(comboValues));
86052
86053             var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120];
86054             var imperialValues = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80];
86055
86056
86057             function maxspeed(selection) {
86058
86059                 var wrap = selection.selectAll('.form-field-input-wrap')
86060                     .data([0]);
86061
86062                 wrap = wrap.enter()
86063                     .append('div')
86064                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
86065                     .merge(wrap);
86066
86067
86068                 input = wrap.selectAll('input.maxspeed-number')
86069                     .data([0]);
86070
86071                 input = input.enter()
86072                     .append('input')
86073                     .attr('type', 'text')
86074                     .attr('class', 'maxspeed-number')
86075                     .attr('id', field.domId)
86076                     .call(utilNoAuto)
86077                     .call(speedCombo)
86078                     .merge(input);
86079
86080                 input
86081                     .on('change', change)
86082                     .on('blur', change);
86083
86084                 var loc = combinedEntityExtent().center();
86085                 _isImperial = roadSpeedUnit(loc) === 'mph';
86086
86087                 unitInput = wrap.selectAll('input.maxspeed-unit')
86088                     .data([0]);
86089
86090                 unitInput = unitInput.enter()
86091                     .append('input')
86092                     .attr('type', 'text')
86093                     .attr('class', 'maxspeed-unit')
86094                     .call(unitCombo)
86095                     .merge(unitInput);
86096
86097                 unitInput
86098                     .on('blur', changeUnits)
86099                     .on('change', changeUnits);
86100
86101
86102                 function changeUnits() {
86103                     _isImperial = utilGetSetValue(unitInput) === 'mph';
86104                     utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86105                     setUnitSuggestions();
86106                     change();
86107                 }
86108             }
86109
86110
86111             function setUnitSuggestions() {
86112                 speedCombo.data((_isImperial ? imperialValues : metricValues).map(comboValues));
86113                 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86114             }
86115
86116
86117             function comboValues(d) {
86118                 return {
86119                     value: d.toString(),
86120                     title: d.toString()
86121                 };
86122             }
86123
86124
86125             function change() {
86126                 var tag = {};
86127                 var value = utilGetSetValue(input).trim();
86128
86129                 // don't override multiple values with blank string
86130                 if (!value && Array.isArray(_tags[field.key])) return;
86131
86132                 if (!value) {
86133                     tag[field.key] = undefined;
86134                 } else if (isNaN(value) || !_isImperial) {
86135                     tag[field.key] = context.cleanTagValue(value);
86136                 } else {
86137                     tag[field.key] = context.cleanTagValue(value + ' mph');
86138                 }
86139
86140                 dispatch$1.call('change', this, tag);
86141             }
86142
86143
86144             maxspeed.tags = function(tags) {
86145                 _tags = tags;
86146
86147                 var value = tags[field.key];
86148                 var isMixed = Array.isArray(value);
86149
86150                 if (!isMixed) {
86151                     if (value && value.indexOf('mph') >= 0) {
86152                         value = parseInt(value, 10).toString();
86153                         _isImperial = true;
86154                     } else if (value) {
86155                         _isImperial = false;
86156                     }
86157                 }
86158
86159                 setUnitSuggestions();
86160
86161                 utilGetSetValue(input, typeof value === 'string' ? value : '')
86162                     .attr('title', isMixed ? value.filter(Boolean).join('\n') : null)
86163                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder())
86164                     .classed('mixed', isMixed);
86165             };
86166
86167
86168             maxspeed.focus = function() {
86169                 input.node().focus();
86170             };
86171
86172
86173             maxspeed.entityIDs = function(val) {
86174                 _entityIDs = val;
86175             };
86176
86177
86178             function combinedEntityExtent() {
86179                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86180             }
86181
86182
86183             return utilRebind(maxspeed, dispatch$1, 'on');
86184         }
86185
86186         function uiFieldRadio(field, context) {
86187             var dispatch$1 = dispatch('change');
86188             var placeholder = select(null);
86189             var wrap = select(null);
86190             var labels = select(null);
86191             var radios = select(null);
86192             var radioData = (field.options || (field.strings && field.strings.options && Object.keys(field.strings.options)) || field.keys).slice();  // shallow copy
86193             var typeField;
86194             var layerField;
86195             var _oldType = {};
86196             var _entityIDs = [];
86197
86198
86199             function selectedKey() {
86200                 var node = wrap.selectAll('.form-field-input-radio label.active input');
86201                 return !node.empty() && node.datum();
86202             }
86203
86204
86205             function radio(selection) {
86206                 selection.classed('preset-radio', true);
86207
86208                 wrap = selection.selectAll('.form-field-input-wrap')
86209                     .data([0]);
86210
86211                 var enter = wrap.enter()
86212                     .append('div')
86213                     .attr('class', 'form-field-input-wrap form-field-input-radio');
86214
86215                 enter
86216                     .append('span')
86217                     .attr('class', 'placeholder');
86218
86219                 wrap = wrap
86220                     .merge(enter);
86221
86222
86223                 placeholder = wrap.selectAll('.placeholder');
86224
86225                 labels = wrap.selectAll('label')
86226                     .data(radioData);
86227
86228                 enter = labels.enter()
86229                     .append('label');
86230
86231                 enter
86232                     .append('input')
86233                     .attr('type', 'radio')
86234                     .attr('name', field.id)
86235                     .attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
86236                     .attr('checked', false);
86237
86238                 enter
86239                     .append('span')
86240                     .text(function(d) { return field.t('options.' + d, { 'default': d }); });
86241
86242                 labels = labels
86243                     .merge(enter);
86244
86245                 radios = labels.selectAll('input')
86246                     .on('change', changeRadio);
86247
86248             }
86249
86250
86251             function structureExtras(selection, tags) {
86252                 var selected = selectedKey() || tags.layer !== undefined;
86253                 var type = _mainPresetIndex.field(selected);
86254                 var layer = _mainPresetIndex.field('layer');
86255                 var showLayer = (selected === 'bridge' || selected === 'tunnel' || tags.layer !== undefined);
86256
86257
86258                 var extrasWrap = selection.selectAll('.structure-extras-wrap')
86259                     .data(selected ? [0] : []);
86260
86261                 extrasWrap.exit()
86262                     .remove();
86263
86264                 extrasWrap = extrasWrap.enter()
86265                     .append('div')
86266                     .attr('class', 'structure-extras-wrap')
86267                     .merge(extrasWrap);
86268
86269                 var list = extrasWrap.selectAll('ul')
86270                     .data([0]);
86271
86272                 list = list.enter()
86273                     .append('ul')
86274                     .attr('class', 'rows')
86275                     .merge(list);
86276
86277
86278                 // Type
86279                 if (type) {
86280                     if (!typeField || typeField.id !== selected) {
86281                         typeField = uiField(context, type, _entityIDs, { wrap: false })
86282                             .on('change', changeType);
86283                     }
86284                     typeField.tags(tags);
86285                 } else {
86286                     typeField = null;
86287                 }
86288
86289                 var typeItem = list.selectAll('.structure-type-item')
86290                     .data(typeField ? [typeField] : [], function(d) { return d.id; });
86291
86292                 // Exit
86293                 typeItem.exit()
86294                     .remove();
86295
86296                 // Enter
86297                 var typeEnter = typeItem.enter()
86298                     .insert('li', ':first-child')
86299                     .attr('class', 'labeled-input structure-type-item');
86300
86301                 typeEnter
86302                     .append('span')
86303                     .attr('class', 'label structure-label-type')
86304                     .attr('for', 'preset-input-' + selected)
86305                     .text(_t('inspector.radio.structure.type'));
86306
86307                 typeEnter
86308                     .append('div')
86309                     .attr('class', 'structure-input-type-wrap');
86310
86311                 // Update
86312                 typeItem = typeItem
86313                     .merge(typeEnter);
86314
86315                 if (typeField) {
86316                     typeItem.selectAll('.structure-input-type-wrap')
86317                         .call(typeField.render);
86318                 }
86319
86320
86321                 // Layer
86322                 if (layer && showLayer) {
86323                     if (!layerField) {
86324                         layerField = uiField(context, layer, _entityIDs, { wrap: false })
86325                             .on('change', changeLayer);
86326                     }
86327                     layerField.tags(tags);
86328                     field.keys = utilArrayUnion(field.keys, ['layer']);
86329                 } else {
86330                     layerField = null;
86331                     field.keys = field.keys.filter(function(k) { return k !== 'layer'; });
86332                 }
86333
86334                 var layerItem = list.selectAll('.structure-layer-item')
86335                     .data(layerField ? [layerField] : []);
86336
86337                 // Exit
86338                 layerItem.exit()
86339                     .remove();
86340
86341                 // Enter
86342                 var layerEnter = layerItem.enter()
86343                     .append('li')
86344                     .attr('class', 'labeled-input structure-layer-item');
86345
86346                 layerEnter
86347                     .append('span')
86348                     .attr('class', 'label structure-label-layer')
86349                     .attr('for', 'preset-input-layer')
86350                     .text(_t('inspector.radio.structure.layer'));
86351
86352                 layerEnter
86353                     .append('div')
86354                     .attr('class', 'structure-input-layer-wrap');
86355
86356                 // Update
86357                 layerItem = layerItem
86358                     .merge(layerEnter);
86359
86360                 if (layerField) {
86361                     layerItem.selectAll('.structure-input-layer-wrap')
86362                         .call(layerField.render);
86363                 }
86364             }
86365
86366
86367             function changeType(t, onInput) {
86368                 var key = selectedKey();
86369                 if (!key) return;
86370
86371                 var val = t[key];
86372                 if (val !== 'no') {
86373                     _oldType[key] = val;
86374                 }
86375
86376                 if (field.type === 'structureRadio') {
86377                     // remove layer if it should not be set
86378                     if (val === 'no' ||
86379                         (key !== 'bridge' && key !== 'tunnel') ||
86380                         (key === 'tunnel' && val === 'building_passage')) {
86381                         t.layer = undefined;
86382                     }
86383                     // add layer if it should be set
86384                     if (t.layer === undefined) {
86385                         if (key === 'bridge' && val !== 'no') {
86386                             t.layer = '1';
86387                         }
86388                         if (key === 'tunnel' && val !== 'no' && val !== 'building_passage') {
86389                             t.layer = '-1';
86390                         }
86391                     }
86392                  }
86393
86394                 dispatch$1.call('change', this, t, onInput);
86395             }
86396
86397
86398             function changeLayer(t, onInput) {
86399                 if (t.layer === '0') {
86400                     t.layer = undefined;
86401                 }
86402                 dispatch$1.call('change', this, t, onInput);
86403             }
86404
86405
86406             function changeRadio() {
86407                 var t = {};
86408                 var activeKey;
86409
86410                 if (field.key) {
86411                     t[field.key] = undefined;
86412                 }
86413
86414                 radios.each(function(d) {
86415                     var active = select(this).property('checked');
86416                     if (active) activeKey = d;
86417
86418                     if (field.key) {
86419                         if (active) t[field.key] = d;
86420                     } else {
86421                         var val = _oldType[activeKey] || 'yes';
86422                         t[d] = active ? val : undefined;
86423                     }
86424                 });
86425
86426                 if (field.type === 'structureRadio') {
86427                     if (activeKey === 'bridge') {
86428                         t.layer = '1';
86429                     } else if (activeKey === 'tunnel' && t.tunnel !== 'building_passage') {
86430                         t.layer = '-1';
86431                     } else {
86432                         t.layer = undefined;
86433                     }
86434                 }
86435
86436                 dispatch$1.call('change', this, t);
86437             }
86438
86439
86440             radio.tags = function(tags) {
86441
86442                 radios.property('checked', function(d) {
86443                     if (field.key) {
86444                         return tags[field.key] === d;
86445                     }
86446                     return !!(typeof tags[d] === 'string' && tags[d].toLowerCase() !== 'no');
86447                 });
86448
86449                 function isMixed(d) {
86450                     if (field.key) {
86451                         return Array.isArray(tags[field.key]) && tags[field.key].includes(d);
86452                     }
86453                     return Array.isArray(tags[d]);
86454                 }
86455
86456                 labels
86457                     .classed('active', function(d) {
86458                         if (field.key) {
86459                             return (Array.isArray(tags[field.key]) && tags[field.key].includes(d))
86460                                 || tags[field.key] === d;
86461                         }
86462                         return Array.isArray(tags[d]) || !!(tags[d] && tags[d].toLowerCase() !== 'no');
86463                     })
86464                     .classed('mixed', isMixed)
86465                     .attr('title', function(d) {
86466                         return isMixed(d) ? _t('inspector.unshared_value_tooltip') : null;
86467                     });
86468
86469
86470                 var selection = radios.filter(function() { return this.checked; });
86471
86472                 if (selection.empty()) {
86473                     placeholder.text(_t('inspector.none'));
86474                 } else {
86475                     placeholder.text(selection.attr('value'));
86476                     _oldType[selection.datum()] = tags[selection.datum()];
86477                 }
86478
86479                 if (field.type === 'structureRadio') {
86480                     // For waterways without a tunnel tag, set 'culvert' as
86481                     // the _oldType to default to if the user picks 'tunnel'
86482                     if (!!tags.waterway && !_oldType.tunnel) {
86483                         _oldType.tunnel = 'culvert';
86484                     }
86485
86486                     wrap.call(structureExtras, tags);
86487                 }
86488             };
86489
86490
86491             radio.focus = function() {
86492                 radios.node().focus();
86493             };
86494
86495
86496             radio.entityIDs = function(val) {
86497                 if (!arguments.length) return _entityIDs;
86498                 _entityIDs = val;
86499                 _oldType = {};
86500                 return radio;
86501             };
86502
86503
86504             radio.isAllowed = function() {
86505                 return _entityIDs.length === 1;
86506             };
86507
86508
86509             return utilRebind(radio, dispatch$1, 'on');
86510         }
86511
86512         function uiFieldRestrictions(field, context) {
86513             var dispatch$1 = dispatch('change');
86514             var breathe = behaviorBreathe();
86515
86516             corePreferences('turn-restriction-via-way', null);                 // remove old key
86517             var storedViaWay = corePreferences('turn-restriction-via-way0');   // use new key #6922
86518             var storedDistance = corePreferences('turn-restriction-distance');
86519
86520             var _maxViaWay = storedViaWay !== null ? (+storedViaWay) : 0;
86521             var _maxDistance = storedDistance ? (+storedDistance) : 30;
86522             var _initialized = false;
86523             var _parent = select(null);       // the entire field
86524             var _container = select(null);    // just the map
86525             var _oldTurns;
86526             var _graph;
86527             var _vertexID;
86528             var _intersection;
86529             var _fromWayID;
86530
86531             var _lastXPos;
86532
86533
86534             function restrictions(selection) {
86535                 _parent = selection;
86536
86537                 // try to reuse the intersection, but always rebuild it if the graph has changed
86538                 if (_vertexID && (context.graph() !== _graph || !_intersection)) {
86539                     _graph = context.graph();
86540                     _intersection = osmIntersection(_graph, _vertexID, _maxDistance);
86541                 }
86542
86543                 // It's possible for there to be no actual intersection here.
86544                 // for example, a vertex of two `highway=path`
86545                 // In this case, hide the field.
86546                 var isOK = (
86547                     _intersection &&
86548                     _intersection.vertices.length &&           // has vertices
86549                     _intersection.vertices                     // has the vertex that the user selected
86550                         .filter(function(vertex) { return vertex.id === _vertexID; }).length &&
86551                     _intersection.ways.length > 2 &&           // has more than 2 ways
86552                     _intersection.ways                         // has more than 1 TO way
86553                         .filter(function(way) { return way.__to; }).length > 1
86554                 );
86555
86556                 // Also hide in the case where
86557                 select(selection.node().parentNode).classed('hide', !isOK);
86558
86559                 // if form field is hidden or has detached from dom, clean up.
86560                 if (!isOK ||
86561                     !context.container().select('.inspector-wrap.inspector-hidden').empty() ||
86562                     !selection.node().parentNode ||
86563                     !selection.node().parentNode.parentNode) {
86564                     selection.call(restrictions.off);
86565                     return;
86566                 }
86567
86568
86569                 var wrap = selection.selectAll('.form-field-input-wrap')
86570                     .data([0]);
86571
86572                 wrap = wrap.enter()
86573                     .append('div')
86574                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
86575                     .merge(wrap);
86576
86577                 var container = wrap.selectAll('.restriction-container')
86578                     .data([0]);
86579
86580                 // enter
86581                 var containerEnter = container.enter()
86582                     .append('div')
86583                     .attr('class', 'restriction-container');
86584
86585                 containerEnter
86586                     .append('div')
86587                     .attr('class', 'restriction-help');
86588
86589                 // update
86590                 _container = containerEnter
86591                     .merge(container)
86592                     .call(renderViewer);
86593
86594                 var controls = wrap.selectAll('.restriction-controls')
86595                     .data([0]);
86596
86597                 // enter/update
86598                 controls.enter()
86599                     .append('div')
86600                     .attr('class', 'restriction-controls-container')
86601                     .append('div')
86602                     .attr('class', 'restriction-controls')
86603                     .merge(controls)
86604                     .call(renderControls);
86605             }
86606
86607
86608             function renderControls(selection) {
86609                 var distControl = selection.selectAll('.restriction-distance')
86610                     .data([0]);
86611
86612                 distControl.exit()
86613                     .remove();
86614
86615                 var distControlEnter = distControl.enter()
86616                     .append('div')
86617                     .attr('class', 'restriction-control restriction-distance');
86618
86619                 distControlEnter
86620                     .append('span')
86621                     .attr('class', 'restriction-control-label restriction-distance-label')
86622                     .text(_t('restriction.controls.distance') + ':');
86623
86624                 distControlEnter
86625                     .append('input')
86626                     .attr('class', 'restriction-distance-input')
86627                     .attr('type', 'range')
86628                     .attr('min', '20')
86629                     .attr('max', '50')
86630                     .attr('step', '5');
86631
86632                 distControlEnter
86633                     .append('span')
86634                     .attr('class', 'restriction-distance-text');
86635
86636                 // update
86637                 selection.selectAll('.restriction-distance-input')
86638                     .property('value', _maxDistance)
86639                     .on('input', function() {
86640                         var val = select(this).property('value');
86641                         _maxDistance = +val;
86642                         _intersection = null;
86643                         _container.selectAll('.layer-osm .layer-turns *').remove();
86644                         corePreferences('turn-restriction-distance', _maxDistance);
86645                         _parent.call(restrictions);
86646                     });
86647
86648                 selection.selectAll('.restriction-distance-text')
86649                     .text(displayMaxDistance(_maxDistance));
86650
86651
86652                 var viaControl = selection.selectAll('.restriction-via-way')
86653                     .data([0]);
86654
86655                 viaControl.exit()
86656                     .remove();
86657
86658                 var viaControlEnter = viaControl.enter()
86659                     .append('div')
86660                     .attr('class', 'restriction-control restriction-via-way');
86661
86662                 viaControlEnter
86663                     .append('span')
86664                     .attr('class', 'restriction-control-label restriction-via-way-label')
86665                     .text(_t('restriction.controls.via') + ':');
86666
86667                 viaControlEnter
86668                     .append('input')
86669                     .attr('class', 'restriction-via-way-input')
86670                     .attr('type', 'range')
86671                     .attr('min', '0')
86672                     .attr('max', '2')
86673                     .attr('step', '1');
86674
86675                 viaControlEnter
86676                     .append('span')
86677                     .attr('class', 'restriction-via-way-text');
86678
86679                 // update
86680                 selection.selectAll('.restriction-via-way-input')
86681                     .property('value', _maxViaWay)
86682                     .on('input', function() {
86683                         var val = select(this).property('value');
86684                         _maxViaWay = +val;
86685                         _container.selectAll('.layer-osm .layer-turns *').remove();
86686                         corePreferences('turn-restriction-via-way0', _maxViaWay);
86687                         _parent.call(restrictions);
86688                     });
86689
86690                 selection.selectAll('.restriction-via-way-text')
86691                     .text(displayMaxVia(_maxViaWay));
86692             }
86693
86694
86695             function renderViewer(selection) {
86696                 if (!_intersection) return;
86697
86698                 var vgraph = _intersection.graph;
86699                 var filter = utilFunctor(true);
86700                 var projection = geoRawMercator();
86701
86702                 // Reflow warning: `utilGetDimensions` calls `getBoundingClientRect`
86703                 // Instead of asking the restriction-container for its dimensions,
86704                 //  we can ask the .sidebar, which can have its dimensions cached.
86705                 // width: calc as sidebar - padding
86706                 // height: hardcoded (from `80_app.css`)
86707                 // var d = utilGetDimensions(selection);
86708                 var sdims = utilGetDimensions(context.container().select('.sidebar'));
86709                 var d = [ sdims[0] - 50, 370 ];
86710                 var c = geoVecScale(d, 0.5);
86711                 var z = 22;
86712
86713                 projection.scale(geoZoomToScale(z));
86714
86715                 // Calculate extent of all key vertices
86716                 var extent = geoExtent();
86717                 for (var i = 0; i < _intersection.vertices.length; i++) {
86718                     extent._extend(_intersection.vertices[i].extent());
86719                 }
86720
86721                 // If this is a large intersection, adjust zoom to fit extent
86722                 if (_intersection.vertices.length > 1) {
86723                     var padding = 180;   // in z22 pixels
86724                     var tl = projection([extent[0][0], extent[1][1]]);
86725                     var br = projection([extent[1][0], extent[0][1]]);
86726                     var hFactor = (br[0] - tl[0]) / (d[0] - padding);
86727                     var vFactor = (br[1] - tl[1]) / (d[1] - padding);
86728                     var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
86729                     var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
86730                     z = z - Math.max(hZoomDiff, vZoomDiff);
86731                     projection.scale(geoZoomToScale(z));
86732                 }
86733
86734                 var padTop = 35;   // reserve top space for hint text
86735                 var extentCenter = projection(extent.center());
86736                 extentCenter[1] = extentCenter[1] - padTop;
86737
86738                 projection
86739                     .translate(geoVecSubtract(c, extentCenter))
86740                     .clipExtent([[0, 0], d]);
86741
86742                 var drawLayers = svgLayers(projection, context).only(['osm','touch']).dimensions(d);
86743                 var drawVertices = svgVertices(projection, context);
86744                 var drawLines = svgLines(projection, context);
86745                 var drawTurns = svgTurns(projection, context);
86746
86747                 var firstTime = selection.selectAll('.surface').empty();
86748
86749                 selection
86750                     .call(drawLayers);
86751
86752                 var surface = selection.selectAll('.surface')
86753                     .classed('tr', true);
86754
86755                 if (firstTime) {
86756                     _initialized = true;
86757
86758                     surface
86759                         .call(breathe);
86760                 }
86761
86762                 // This can happen if we've lowered the detail while a FROM way
86763                 // is selected, and that way is no longer part of the intersection.
86764                 if (_fromWayID && !vgraph.hasEntity(_fromWayID)) {
86765                     _fromWayID = null;
86766                     _oldTurns = null;
86767                 }
86768
86769                 surface
86770                     .call(utilSetDimensions, d)
86771                     .call(drawVertices, vgraph, _intersection.vertices, filter, extent, z)
86772                     .call(drawLines, vgraph, _intersection.ways, filter)
86773                     .call(drawTurns, vgraph, _intersection.turns(_fromWayID, _maxViaWay));
86774
86775                 surface
86776                     .on('click.restrictions', click)
86777                     .on('mouseover.restrictions', mouseover);
86778
86779                 surface
86780                     .selectAll('.selected')
86781                     .classed('selected', false);
86782
86783                 surface
86784                     .selectAll('.related')
86785                     .classed('related', false);
86786
86787                 if (_fromWayID) {
86788                     var way = vgraph.entity(_fromWayID);
86789                     surface
86790                         .selectAll('.' + _fromWayID)
86791                         .classed('selected', true)
86792                         .classed('related', true);
86793                 }
86794
86795                 document.addEventListener('resizeWindow', function () {
86796                     utilSetDimensions(_container, null);
86797                     redraw(1);
86798                 }, false);
86799
86800                 updateHints(null);
86801
86802
86803                 function click() {
86804                     surface
86805                         .call(breathe.off)
86806                         .call(breathe);
86807
86808                     var datum = event.target.__data__;
86809                     var entity = datum && datum.properties && datum.properties.entity;
86810                     if (entity) {
86811                         datum = entity;
86812                     }
86813
86814                     if (datum instanceof osmWay && (datum.__from || datum.__via)) {
86815                         _fromWayID = datum.id;
86816                         _oldTurns = null;
86817                         redraw();
86818
86819                     } else if (datum instanceof osmTurn) {
86820                         var actions, extraActions, turns, i;
86821                         var restrictionType = osmInferRestriction(vgraph, datum, projection);
86822
86823                         if (datum.restrictionID && !datum.direct) {
86824                             return;
86825
86826                         } else if (datum.restrictionID && !datum.only) {    // NO -> ONLY
86827                             var seen = {};
86828                             var datumOnly = JSON.parse(JSON.stringify(datum));   // deep clone the datum
86829                             datumOnly.only = true;                               // but change this property
86830                             restrictionType = restrictionType.replace(/^no/, 'only');
86831
86832                             // Adding an ONLY restriction should destroy all other direct restrictions from the FROM towards the VIA.
86833                             // We will remember them in _oldTurns, and restore them if the user clicks again.
86834                             turns = _intersection.turns(_fromWayID, 2);
86835                             extraActions = [];
86836                             _oldTurns = [];
86837                             for (i = 0; i < turns.length; i++) {
86838                                 var turn = turns[i];
86839                                 if (seen[turn.restrictionID]) continue;  // avoid deleting the turn twice (#4968, #4928)
86840
86841                                 if (turn.direct && turn.path[1] === datum.path[1]) {
86842                                     seen[turns[i].restrictionID] = true;
86843                                     turn.restrictionType = osmInferRestriction(vgraph, turn, projection);
86844                                     _oldTurns.push(turn);
86845                                     extraActions.push(actionUnrestrictTurn(turn));
86846                                 }
86847                             }
86848
86849                             actions = _intersection.actions.concat(extraActions, [
86850                                 actionRestrictTurn(datumOnly, restrictionType),
86851                                 _t('operations.restriction.annotation.create')
86852                             ]);
86853
86854                         } else if (datum.restrictionID) {   // ONLY -> Allowed
86855                             // Restore whatever restrictions we might have destroyed by cycling thru the ONLY state.
86856                             // This relies on the assumption that the intersection was already split up when we
86857                             // performed the previous action (NO -> ONLY), so the IDs in _oldTurns shouldn't have changed.
86858                             turns = _oldTurns || [];
86859                             extraActions = [];
86860                             for (i = 0; i < turns.length; i++) {
86861                                 if (turns[i].key !== datum.key) {
86862                                     extraActions.push(actionRestrictTurn(turns[i], turns[i].restrictionType));
86863                                 }
86864                             }
86865                             _oldTurns = null;
86866
86867                             actions = _intersection.actions.concat(extraActions, [
86868                                 actionUnrestrictTurn(datum),
86869                                 _t('operations.restriction.annotation.delete')
86870                             ]);
86871
86872                         } else {    // Allowed -> NO
86873                             actions = _intersection.actions.concat([
86874                                 actionRestrictTurn(datum, restrictionType),
86875                                 _t('operations.restriction.annotation.create')
86876                             ]);
86877                         }
86878
86879                         context.perform.apply(context, actions);
86880
86881                         // At this point the datum will be changed, but will have same key..
86882                         // Refresh it and update the help..
86883                         var s = surface.selectAll('.' + datum.key);
86884                         datum = s.empty() ? null : s.datum();
86885                         updateHints(datum);
86886
86887                     } else {
86888                         _fromWayID = null;
86889                         _oldTurns = null;
86890                         redraw();
86891                     }
86892                 }
86893
86894
86895                 function mouseover() {
86896                     var datum = event.target.__data__;
86897                     updateHints(datum);
86898                 }
86899
86900                 _lastXPos = _lastXPos || sdims[0];
86901
86902                 function redraw(minChange) {
86903                     var xPos = -1;
86904
86905                     if (minChange) {
86906                         xPos = utilGetDimensions(context.container().select('.sidebar'))[0];
86907                     }
86908
86909                     if (!minChange || (minChange && Math.abs(xPos - _lastXPos) >= minChange)) {
86910                         if (context.hasEntity(_vertexID)) {
86911                             _lastXPos = xPos;
86912                             _container.call(renderViewer);
86913                         }
86914                     }
86915                 }
86916
86917
86918                 function highlightPathsFrom(wayID) {
86919                     surface.selectAll('.related')
86920                         .classed('related', false)
86921                         .classed('allow', false)
86922                         .classed('restrict', false)
86923                         .classed('only', false);
86924
86925                     surface.selectAll('.' + wayID)
86926                         .classed('related', true);
86927
86928                     if (wayID) {
86929                         var turns = _intersection.turns(wayID, _maxViaWay);
86930                         for (var i = 0; i < turns.length; i++) {
86931                             var turn = turns[i];
86932                             var ids = [turn.to.way];
86933                             var klass = (turn.no ? 'restrict' : (turn.only ? 'only' : 'allow'));
86934
86935                             if (turn.only || turns.length === 1) {
86936                                 if (turn.via.ways) {
86937                                     ids = ids.concat(turn.via.ways);
86938                                 }
86939                             } else if (turn.to.way === wayID) {
86940                                 continue;
86941                             }
86942
86943                             surface.selectAll(utilEntitySelector(ids))
86944                                 .classed('related', true)
86945                                 .classed('allow', (klass === 'allow'))
86946                                 .classed('restrict', (klass === 'restrict'))
86947                                 .classed('only', (klass === 'only'));
86948                         }
86949                     }
86950                 }
86951
86952
86953                 function updateHints(datum) {
86954                     var help = _container.selectAll('.restriction-help').html('');
86955
86956                     var placeholders = {};
86957                     ['from', 'via', 'to'].forEach(function(k) {
86958                         placeholders[k] = '<span class="qualifier">' + _t('restriction.help.' + k) + '</span>';
86959                     });
86960
86961                     var entity = datum && datum.properties && datum.properties.entity;
86962                     if (entity) {
86963                         datum = entity;
86964                     }
86965
86966                     if (_fromWayID) {
86967                         way = vgraph.entity(_fromWayID);
86968                         surface
86969                             .selectAll('.' + _fromWayID)
86970                             .classed('selected', true)
86971                             .classed('related', true);
86972                     }
86973
86974                     // Hovering a way
86975                     if (datum instanceof osmWay && datum.__from) {
86976                         way = datum;
86977
86978                         highlightPathsFrom(_fromWayID ? null : way.id);
86979                         surface.selectAll('.' + way.id)
86980                             .classed('related', true);
86981
86982                         var clickSelect = (!_fromWayID || _fromWayID !== way.id);
86983                         help
86984                             .append('div')      // "Click to select FROM {fromName}." / "FROM {fromName}"
86985                             .html(_t('restriction.help.' + (clickSelect ? 'select_from_name' : 'from_name'), {
86986                                 from: placeholders.from,
86987                                 fromName: displayName(way.id, vgraph)
86988                             }));
86989
86990
86991                     // Hovering a turn arrow
86992                     } else if (datum instanceof osmTurn) {
86993                         var restrictionType = osmInferRestriction(vgraph, datum, projection);
86994                         var turnType = restrictionType.replace(/^(only|no)\_/, '');
86995                         var indirect = (datum.direct === false ? _t('restriction.help.indirect') : '');
86996                         var klass, turnText, nextText;
86997
86998                         if (datum.no) {
86999                             klass = 'restrict';
87000                             turnText = _t('restriction.help.turn.no_' + turnType, { indirect: indirect });
87001                             nextText = _t('restriction.help.turn.only_' + turnType, { indirect: '' });
87002                         } else if (datum.only) {
87003                             klass = 'only';
87004                             turnText = _t('restriction.help.turn.only_' + turnType, { indirect: indirect });
87005                             nextText = _t('restriction.help.turn.allowed_' + turnType, { indirect: '' });
87006                         } else {
87007                             klass = 'allow';
87008                             turnText = _t('restriction.help.turn.allowed_' + turnType, { indirect: indirect });
87009                             nextText = _t('restriction.help.turn.no_' + turnType, { indirect: '' });
87010                         }
87011
87012                         help
87013                             .append('div')      // "NO Right Turn (indirect)"
87014                             .attr('class', 'qualifier ' + klass)
87015                             .text(turnText);
87016
87017                         help
87018                             .append('div')      // "FROM {fromName} TO {toName}"
87019                             .html(_t('restriction.help.from_name_to_name', {
87020                                 from: placeholders.from,
87021                                 fromName: displayName(datum.from.way, vgraph),
87022                                 to: placeholders.to,
87023                                 toName: displayName(datum.to.way, vgraph)
87024                             }));
87025
87026                         if (datum.via.ways && datum.via.ways.length) {
87027                             var names = [];
87028                             for (var i = 0; i < datum.via.ways.length; i++) {
87029                                 var prev = names[names.length - 1];
87030                                 var curr = displayName(datum.via.ways[i], vgraph);
87031                                 if (!prev || curr !== prev)   // collapse identical names
87032                                     names.push(curr);
87033                             }
87034
87035                             help
87036                                 .append('div')      // "VIA {viaNames}"
87037                                 .html(_t('restriction.help.via_names', {
87038                                     via: placeholders.via,
87039                                     viaNames: names.join(', ')
87040                                 }));
87041                         }
87042
87043                         if (!indirect) {
87044                             help
87045                                 .append('div')      // Click for "No Right Turn"
87046                                 .text(_t('restriction.help.toggle', { turn: nextText.trim() }));
87047                         }
87048
87049                         highlightPathsFrom(null);
87050                         var alongIDs = datum.path.slice();
87051                         surface.selectAll(utilEntitySelector(alongIDs))
87052                             .classed('related', true)
87053                             .classed('allow', (klass === 'allow'))
87054                             .classed('restrict', (klass === 'restrict'))
87055                             .classed('only', (klass === 'only'));
87056
87057
87058                     // Hovering empty surface
87059                     } else {
87060                         highlightPathsFrom(null);
87061                         if (_fromWayID) {
87062                             help
87063                                 .append('div')      // "FROM {fromName}"
87064                                 .html(_t('restriction.help.from_name', {
87065                                     from: placeholders.from,
87066                                     fromName: displayName(_fromWayID, vgraph)
87067                                 }));
87068
87069                         } else {
87070                             help
87071                                 .append('div')      // "Click to select a FROM segment."
87072                                 .html(_t('restriction.help.select_from', {
87073                                     from: placeholders.from
87074                                 }));
87075                         }
87076                     }
87077                 }
87078             }
87079
87080
87081             function displayMaxDistance(maxDist) {
87082                 var isImperial = !_mainLocalizer.usesMetric();
87083                 var opts;
87084
87085                 if (isImperial) {
87086                     var distToFeet = {   // imprecise conversion for prettier display
87087                         20: 70, 25: 85, 30: 100, 35: 115, 40: 130, 45: 145, 50: 160
87088                     }[maxDist];
87089                     opts = { distance: _t('units.feet', { quantity: distToFeet }) };
87090                 } else {
87091                     opts = { distance: _t('units.meters', { quantity: maxDist }) };
87092                 }
87093
87094                 return _t('restriction.controls.distance_up_to', opts);
87095             }
87096
87097
87098             function displayMaxVia(maxVia) {
87099                 return maxVia === 0 ? _t('restriction.controls.via_node_only')
87100                     : maxVia === 1 ? _t('restriction.controls.via_up_to_one')
87101                     : _t('restriction.controls.via_up_to_two');
87102             }
87103
87104
87105             function displayName(entityID, graph) {
87106                 var entity = graph.entity(entityID);
87107                 var name = utilDisplayName(entity) || '';
87108                 var matched = _mainPresetIndex.match(entity, graph);
87109                 var type = (matched && matched.name()) || utilDisplayType(entity.id);
87110                 return name || type;
87111             }
87112
87113
87114             restrictions.entityIDs = function(val) {
87115                 _intersection = null;
87116                 _fromWayID = null;
87117                 _oldTurns = null;
87118                 _vertexID = val[0];
87119             };
87120
87121
87122             restrictions.tags = function() {};
87123             restrictions.focus = function() {};
87124
87125
87126             restrictions.off = function(selection) {
87127                 if (!_initialized) return;
87128
87129                 selection.selectAll('.surface')
87130                     .call(breathe.off)
87131                     .on('click.restrictions', null)
87132                     .on('mouseover.restrictions', null);
87133
87134                 select(window)
87135                     .on('resize.restrictions', null);
87136             };
87137
87138
87139             return utilRebind(restrictions, dispatch$1, 'on');
87140         }
87141
87142         uiFieldRestrictions.supportsMultiselection = false;
87143
87144         function uiFieldTextarea(field, context) {
87145             var dispatch$1 = dispatch('change');
87146             var input = select(null);
87147             var _tags;
87148
87149
87150             function textarea(selection) {
87151                 var wrap = selection.selectAll('.form-field-input-wrap')
87152                     .data([0]);
87153
87154                 wrap = wrap.enter()
87155                     .append('div')
87156                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
87157                     .merge(wrap);
87158
87159                 input = wrap.selectAll('textarea')
87160                     .data([0]);
87161
87162                 input = input.enter()
87163                     .append('textarea')
87164                     .attr('id', field.domId)
87165                     .call(utilNoAuto)
87166                     .on('input', change(true))
87167                     .on('blur', change())
87168                     .on('change', change())
87169                     .merge(input);
87170             }
87171
87172
87173             function change(onInput) {
87174                 return function() {
87175
87176                     var val = utilGetSetValue(input);
87177                     if (!onInput) val = context.cleanTagValue(val);
87178
87179                     // don't override multiple values with blank string
87180                     if (!val && Array.isArray(_tags[field.key])) return;
87181
87182                     var t = {};
87183                     t[field.key] = val || undefined;
87184                     dispatch$1.call('change', this, t, onInput);
87185                 };
87186             }
87187
87188
87189             textarea.tags = function(tags) {
87190                 _tags = tags;
87191
87192                 var isMixed = Array.isArray(tags[field.key]);
87193
87194                 utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '')
87195                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
87196                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : (field.placeholder() || _t('inspector.unknown')))
87197                     .classed('mixed', isMixed);
87198             };
87199
87200
87201             textarea.focus = function() {
87202                 input.node().focus();
87203             };
87204
87205
87206             return utilRebind(textarea, dispatch$1, 'on');
87207         }
87208
87209         function uiFieldWikidata(field, context) {
87210             var wikidata = services.wikidata;
87211             var dispatch$1 = dispatch('change');
87212
87213             var _selection = select(null);
87214             var _searchInput = select(null);
87215             var _qid = null;
87216             var _wikidataEntity = null;
87217             var _wikiURL = '';
87218             var _entityIDs = [];
87219
87220             var _wikipediaKey = field.keys && field.keys.find(function(key) {
87221                     return key.includes('wikipedia');
87222                 }),
87223                 _hintKey = field.key === 'wikidata' ? 'name' : field.key.split(':')[0];
87224
87225             var combobox = uiCombobox(context, 'combo-' + field.safeid)
87226                 .caseSensitive(true)
87227                 .minItems(1);
87228
87229             function wiki(selection) {
87230
87231                 _selection = selection;
87232
87233                 var wrap = selection.selectAll('.form-field-input-wrap')
87234                     .data([0]);
87235
87236                 wrap = wrap.enter()
87237                     .append('div')
87238                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
87239                     .merge(wrap);
87240
87241
87242                 var list = wrap.selectAll('ul')
87243                     .data([0]);
87244
87245                 list = list.enter()
87246                     .append('ul')
87247                     .attr('class', 'rows')
87248                     .merge(list);
87249
87250                 var searchRow = list.selectAll('li.wikidata-search')
87251                     .data([0]);
87252
87253                 var searchRowEnter = searchRow.enter()
87254                     .append('li')
87255                     .attr('class', 'wikidata-search');
87256
87257                 searchRowEnter
87258                     .append('input')
87259                     .attr('type', 'text')
87260                     .attr('id', field.domId)
87261                     .style('flex', '1')
87262                     .call(utilNoAuto)
87263                     .on('focus', function() {
87264                         var node = select(this).node();
87265                         node.setSelectionRange(0, node.value.length);
87266                     })
87267                     .on('blur', function() {
87268                         setLabelForEntity();
87269                     })
87270                     .call(combobox.fetcher(fetchWikidataItems));
87271
87272                 combobox.on('accept', function(d) {
87273                     _qid = d.id;
87274                     change();
87275                 }).on('cancel', function() {
87276                     setLabelForEntity();
87277                 });
87278
87279                 searchRowEnter
87280                     .append('button')
87281                     .attr('class', 'form-field-button wiki-link')
87282                     .attr('title', _t('icons.view_on', { domain: 'wikidata.org' }))
87283                     .attr('tabindex', -1)
87284                     .call(svgIcon('#iD-icon-out-link'))
87285                     .on('click', function() {
87286                         event.preventDefault();
87287                         if (_wikiURL) window.open(_wikiURL, '_blank');
87288                     });
87289
87290                 searchRow = searchRow.merge(searchRowEnter);
87291
87292                 _searchInput = searchRow.select('input');
87293
87294                 var wikidataProperties = ['description', 'identifier'];
87295
87296                 var items = list.selectAll('li.labeled-input')
87297                     .data(wikidataProperties);
87298
87299                 // Enter
87300                 var enter = items.enter()
87301                     .append('li')
87302                     .attr('class', function(d) { return 'labeled-input preset-wikidata-' + d; });
87303
87304                 enter
87305                     .append('span')
87306                     .attr('class', 'label')
87307                     .text(function(d) { return _t('wikidata.' + d); });
87308
87309                 enter
87310                     .append('input')
87311                     .attr('type', 'text')
87312                     .call(utilNoAuto)
87313                     .classed('disabled', 'true')
87314                     .attr('readonly', 'true');
87315
87316                 enter
87317                     .append('button')
87318                     .attr('class', 'form-field-button')
87319                     .attr('title', _t('icons.copy'))
87320                     .attr('tabindex', -1)
87321                     .call(svgIcon('#iD-operation-copy'))
87322                     .on('click', function() {
87323                         event.preventDefault();
87324                         select(this.parentNode)
87325                             .select('input')
87326                             .node()
87327                             .select();
87328                         document.execCommand('copy');
87329                     });
87330
87331             }
87332
87333             function fetchWikidataItems(q, callback) {
87334
87335                 if (!q && _hintKey) {
87336                     // other tags may be good search terms
87337                     for (var i in _entityIDs) {
87338                         var entity = context.hasEntity(_entityIDs[i]);
87339                         if (entity.tags[_hintKey]) {
87340                             q = entity.tags[_hintKey];
87341                             break;
87342                         }
87343                     }
87344                 }
87345
87346                 wikidata.itemsForSearchQuery(q, function(err, data) {
87347                     if (err) return;
87348
87349                     for (var i in data) {
87350                         data[i].value = data[i].label + ' (' +  data[i].id + ')';
87351                         data[i].title = data[i].description;
87352                     }
87353
87354                     if (callback) callback(data);
87355                 });
87356             }
87357
87358
87359             function change() {
87360                 var syncTags = {};
87361                 syncTags[field.key] = _qid;
87362                 dispatch$1.call('change', this, syncTags);
87363
87364                 // attempt asynchronous update of wikidata tag..
87365                 var initGraph = context.graph();
87366                 var initEntityIDs = _entityIDs;
87367
87368                 wikidata.entityByQID(_qid, function(err, entity) {
87369                     if (err) return;
87370
87371                     // If graph has changed, we can't apply this update.
87372                     if (context.graph() !== initGraph) return;
87373
87374                     if (!entity.sitelinks) return;
87375
87376                     var langs = wikidata.languagesToQuery();
87377                     // use the label and description languages as fallbacks
87378                     ['labels', 'descriptions'].forEach(function(key) {
87379                         if (!entity[key]) return;
87380
87381                         var valueLangs = Object.keys(entity[key]);
87382                         if (valueLangs.length === 0) return;
87383                         var valueLang = valueLangs[0];
87384
87385                         if (langs.indexOf(valueLang) === -1) {
87386                             langs.push(valueLang);
87387                         }
87388                     });
87389
87390                     var newWikipediaValue;
87391
87392                     if (_wikipediaKey) {
87393                         var foundPreferred;
87394                         for (var i in langs) {
87395                             var lang = langs[i];
87396                             var siteID = lang.replace('-', '_') + 'wiki';
87397                             if (entity.sitelinks[siteID]) {
87398                                 foundPreferred = true;
87399                                 newWikipediaValue = lang + ':' + entity.sitelinks[siteID].title;
87400                                 // use the first match
87401                                 break;
87402                             }
87403                         }
87404
87405                         if (!foundPreferred) {
87406                             // No wikipedia sites available in the user's language or the fallback languages,
87407                             // default to any wikipedia sitelink
87408
87409                             var wikiSiteKeys = Object.keys(entity.sitelinks).filter(function(site) {
87410                                 return site.endsWith('wiki');
87411                             });
87412
87413                             if (wikiSiteKeys.length === 0) {
87414                                 // if no wikipedia pages are linked to this wikidata entity, delete that tag
87415                                 newWikipediaValue = null;
87416                             } else {
87417                                 var wikiLang = wikiSiteKeys[0].slice(0, -4).replace('_', '-');
87418                                 var wikiTitle = entity.sitelinks[wikiSiteKeys[0]].title;
87419                                 newWikipediaValue = wikiLang + ':' + wikiTitle;
87420                             }
87421                         }
87422                     }
87423
87424                     if (newWikipediaValue) {
87425                         newWikipediaValue = context.cleanTagValue(newWikipediaValue);
87426                     }
87427
87428                     if (typeof newWikipediaValue === 'undefined') return;
87429
87430                     var actions = initEntityIDs.map(function(entityID) {
87431                         var entity = context.hasEntity(entityID);
87432                         if (!entity) return;
87433
87434                         var currTags = Object.assign({}, entity.tags);  // shallow copy
87435                         if (newWikipediaValue === null) {
87436                             if (!currTags[_wikipediaKey]) return;
87437
87438                             delete currTags[_wikipediaKey];
87439                         } else {
87440                             currTags[_wikipediaKey] = newWikipediaValue;
87441                         }
87442
87443                         return actionChangeTags(entityID, currTags);
87444                     }).filter(Boolean);
87445
87446                     if (!actions.length) return;
87447
87448                     // Coalesce the update of wikidata tag into the previous tag change
87449                     context.overwrite(
87450                         function actionUpdateWikipediaTags(graph) {
87451                             actions.forEach(function(action) {
87452                                 graph = action(graph);
87453                             });
87454                             return graph;
87455                         },
87456                         context.history().undoAnnotation()
87457                     );
87458
87459                     // do not dispatch.call('change') here, because entity_editor
87460                     // changeTags() is not intended to be called asynchronously
87461                 });
87462             }
87463
87464             function setLabelForEntity() {
87465                 var label = '';
87466                 if (_wikidataEntity) {
87467                     label = entityPropertyForDisplay(_wikidataEntity, 'labels');
87468                     if (label.length === 0) {
87469                         label = _wikidataEntity.id.toString();
87470                     }
87471                 }
87472                 utilGetSetValue(_searchInput, label);
87473             }
87474
87475
87476             wiki.tags = function(tags) {
87477
87478                 var isMixed = Array.isArray(tags[field.key]);
87479                 _searchInput
87480                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : null)
87481                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : '')
87482                     .classed('mixed', isMixed);
87483
87484                 _qid = typeof tags[field.key] === 'string' && tags[field.key] || '';
87485
87486                 if (!/^Q[0-9]*$/.test(_qid)) {   // not a proper QID
87487                     unrecognized();
87488                     return;
87489                 }
87490
87491                 // QID value in correct format
87492                 _wikiURL = 'https://wikidata.org/wiki/' + _qid;
87493                 wikidata.entityByQID(_qid, function(err, entity) {
87494                     if (err) {
87495                         unrecognized();
87496                         return;
87497                     }
87498                     _wikidataEntity = entity;
87499
87500                     setLabelForEntity();
87501
87502                     var description = entityPropertyForDisplay(entity, 'descriptions');
87503
87504                     _selection.select('button.wiki-link')
87505                         .classed('disabled', false);
87506
87507                     _selection.select('.preset-wikidata-description')
87508                         .style('display', function(){
87509                             return description.length > 0 ? 'flex' : 'none';
87510                         })
87511                         .select('input')
87512                         .attr('value', description);
87513
87514                     _selection.select('.preset-wikidata-identifier')
87515                         .style('display', function(){
87516                             return entity.id ? 'flex' : 'none';
87517                         })
87518                         .select('input')
87519                         .attr('value', entity.id);
87520                 });
87521
87522
87523                 // not a proper QID
87524                 function unrecognized() {
87525                     _wikidataEntity = null;
87526                     setLabelForEntity();
87527
87528                     _selection.select('.preset-wikidata-description')
87529                         .style('display', 'none');
87530                     _selection.select('.preset-wikidata-identifier')
87531                         .style('display', 'none');
87532
87533                     _selection.select('button.wiki-link')
87534                         .classed('disabled', true);
87535
87536                     if (_qid && _qid !== '') {
87537                         _wikiURL = 'https://wikidata.org/wiki/Special:Search?search=' + _qid;
87538                     } else {
87539                         _wikiURL = '';
87540                     }
87541                 }
87542             };
87543
87544             function entityPropertyForDisplay(wikidataEntity, propKey) {
87545                 if (!wikidataEntity[propKey]) return '';
87546                 var propObj = wikidataEntity[propKey];
87547                 var langKeys = Object.keys(propObj);
87548                 if (langKeys.length === 0) return '';
87549                 // sorted by priority, since we want to show the user's language first if possible
87550                 var langs = wikidata.languagesToQuery();
87551                 for (var i in langs) {
87552                     var lang = langs[i];
87553                     var valueObj = propObj[lang];
87554                     if (valueObj && valueObj.value && valueObj.value.length > 0) return valueObj.value;
87555                 }
87556                 // default to any available value
87557                 return propObj[langKeys[0]].value;
87558             }
87559
87560
87561             wiki.entityIDs = function(val) {
87562                 if (!arguments.length) return _entityIDs;
87563                 _entityIDs = val;
87564                 return wiki;
87565             };
87566
87567
87568             wiki.focus = function() {
87569                 _searchInput.node().focus();
87570             };
87571
87572
87573             return utilRebind(wiki, dispatch$1, 'on');
87574         }
87575
87576         function uiFieldWikipedia(field, context) {
87577           const dispatch$1 = dispatch('change');
87578           const wikipedia = services.wikipedia;
87579           const wikidata = services.wikidata;
87580           let _langInput = select(null);
87581           let _titleInput = select(null);
87582           let _wikiURL = '';
87583           let _entityIDs;
87584           let _tags;
87585
87586           let _dataWikipedia = [];
87587           _mainFileFetcher.get('wmf_sitematrix')
87588             .then(d => {
87589               _dataWikipedia = d;
87590               if (_tags) updateForTags(_tags);
87591             })
87592             .catch(() => { /* ignore */ });
87593
87594
87595           const langCombo = uiCombobox(context, 'wikipedia-lang')
87596             .fetcher((value, callback) => {
87597               const v = value.toLowerCase();
87598               callback(_dataWikipedia
87599                 .filter(d => {
87600                   return d[0].toLowerCase().indexOf(v) >= 0 ||
87601                     d[1].toLowerCase().indexOf(v) >= 0 ||
87602                     d[2].toLowerCase().indexOf(v) >= 0;
87603                 })
87604                 .map(d => ({ value: d[1] }))
87605               );
87606             });
87607
87608           const titleCombo = uiCombobox(context, 'wikipedia-title')
87609             .fetcher((value, callback) => {
87610               if (!value) {
87611                 value = '';
87612                 for (let i in _entityIDs) {
87613                   let entity = context.hasEntity(_entityIDs[i]);
87614                   if (entity.tags.name) {
87615                     value = entity.tags.name;
87616                     break;
87617                   }
87618                 }
87619               }
87620               const searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
87621               searchfn(language()[2], value, (query, data) => {
87622                 callback( data.map(d => ({ value: d })) );
87623               });
87624             });
87625
87626
87627           function wiki(selection) {
87628             let wrap = selection.selectAll('.form-field-input-wrap')
87629               .data([0]);
87630
87631             wrap = wrap.enter()
87632               .append('div')
87633               .attr('class', `form-field-input-wrap form-field-input-${field.type}`)
87634               .merge(wrap);
87635
87636
87637             let langContainer = wrap.selectAll('.wiki-lang-container')
87638               .data([0]);
87639
87640             langContainer = langContainer.enter()
87641               .append('div')
87642               .attr('class', 'wiki-lang-container')
87643               .merge(langContainer);
87644
87645
87646             _langInput = langContainer.selectAll('input.wiki-lang')
87647               .data([0]);
87648
87649             _langInput = _langInput.enter()
87650               .append('input')
87651               .attr('type', 'text')
87652               .attr('class', 'wiki-lang')
87653               .attr('placeholder', _t('translate.localized_translation_language'))
87654               .call(utilNoAuto)
87655               .call(langCombo)
87656               .merge(_langInput);
87657
87658             utilGetSetValue(_langInput, language()[1]);
87659
87660             _langInput
87661               .on('blur', changeLang)
87662               .on('change', changeLang);
87663
87664
87665             let titleContainer = wrap.selectAll('.wiki-title-container')
87666               .data([0]);
87667
87668             titleContainer = titleContainer.enter()
87669               .append('div')
87670               .attr('class', 'wiki-title-container')
87671               .merge(titleContainer);
87672
87673             _titleInput = titleContainer.selectAll('input.wiki-title')
87674               .data([0]);
87675
87676             _titleInput = _titleInput.enter()
87677               .append('input')
87678               .attr('type', 'text')
87679               .attr('class', 'wiki-title')
87680               .attr('id', field.domId)
87681               .call(utilNoAuto)
87682               .call(titleCombo)
87683               .merge(_titleInput);
87684
87685             _titleInput
87686               .on('blur', blur)
87687               .on('change', change);
87688
87689
87690             let link = titleContainer.selectAll('.wiki-link')
87691               .data([0]);
87692
87693             link = link.enter()
87694               .append('button')
87695               .attr('class', 'form-field-button wiki-link')
87696               .attr('tabindex', -1)
87697               .attr('title', _t('icons.view_on', { domain: 'wikipedia.org' }))
87698               .call(svgIcon('#iD-icon-out-link'))
87699               .merge(link);
87700
87701             link
87702               .on('click', () => {
87703                 event.preventDefault();
87704                 if (_wikiURL) window.open(_wikiURL, '_blank');
87705               });
87706           }
87707
87708
87709           function language() {
87710             const value = utilGetSetValue(_langInput).toLowerCase();
87711             const locale = _mainLocalizer.localeCode().toLowerCase();
87712             let localeLanguage;
87713             return _dataWikipedia.find(d => {
87714               if (d[2] === locale) localeLanguage = d;
87715               return d[0].toLowerCase() === value || d[1].toLowerCase() === value || d[2] === value;
87716             }) || localeLanguage || ['English', 'English', 'en'];
87717           }
87718
87719
87720           function changeLang() {
87721             utilGetSetValue(_langInput, language()[1]);
87722             change(true);
87723           }
87724
87725
87726           function blur() {
87727             change(true);
87728           }
87729
87730
87731           function change(skipWikidata) {
87732             let value = utilGetSetValue(_titleInput);
87733             const m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/);
87734             const l = m && _dataWikipedia.find(d => m[1] === d[2]);
87735             let syncTags = {};
87736
87737             if (l) {
87738               // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
87739               value = decodeURIComponent(m[2]).replace(/_/g, ' ');
87740               if (m[3]) {
87741                 let anchor;
87742                 // try {
87743                 // leave this out for now - #6232
87744                   // Best-effort `anchordecode:` implementation
87745                   // anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
87746                 // } catch (e) {
87747                 anchor = decodeURIComponent(m[3]);
87748                 // }
87749                 value += '#' + anchor.replace(/_/g, ' ');
87750               }
87751               value = value.slice(0, 1).toUpperCase() + value.slice(1);
87752               utilGetSetValue(_langInput, l[1]);
87753               utilGetSetValue(_titleInput, value);
87754             }
87755
87756             if (value) {
87757               syncTags.wikipedia = context.cleanTagValue(language()[2] + ':' + value);
87758             } else {
87759               syncTags.wikipedia = undefined;
87760             }
87761
87762             dispatch$1.call('change', this, syncTags);
87763
87764
87765             if (skipWikidata || !value || !language()[2]) return;
87766
87767             // attempt asynchronous update of wikidata tag..
87768             const initGraph = context.graph();
87769             const initEntityIDs = _entityIDs;
87770
87771             wikidata.itemsByTitle(language()[2], value, (err, data) => {
87772               if (err || !data || !Object.keys(data).length) return;
87773
87774               // If graph has changed, we can't apply this update.
87775               if (context.graph() !== initGraph) return;
87776
87777               const qids = Object.keys(data);
87778               const value = qids && qids.find(id => id.match(/^Q\d+$/));
87779
87780               let actions = initEntityIDs.map((entityID) => {
87781                 let entity = context.entity(entityID).tags;
87782                 let currTags = Object.assign({}, entity);  // shallow copy
87783                 if (currTags.wikidata !== value) {
87784                     currTags.wikidata = value;
87785                     return actionChangeTags(entityID, currTags);
87786                 }
87787               }).filter(Boolean);
87788
87789               if (!actions.length) return;
87790
87791               // Coalesce the update of wikidata tag into the previous tag change
87792               context.overwrite(
87793                 function actionUpdateWikidataTags(graph) {
87794                   actions.forEach(function(action) {
87795                     graph = action(graph);
87796                   });
87797                   return graph;
87798                 },
87799                 context.history().undoAnnotation()
87800               );
87801
87802               // do not dispatch.call('change') here, because entity_editor
87803               // changeTags() is not intended to be called asynchronously
87804             });
87805           }
87806
87807
87808           wiki.tags = (tags) => {
87809             _tags = tags;
87810             updateForTags(tags);
87811           };
87812
87813           function updateForTags(tags) {
87814
87815             const value = typeof tags[field.key] === 'string' ? tags[field.key] : '';
87816             const m = value.match(/([^:]+):([^#]+)(?:#(.+))?/);
87817             const l = m && _dataWikipedia.find(d => m[1] === d[2]);
87818             let anchor = m && m[3];
87819
87820             // value in correct format
87821             if (l) {
87822               utilGetSetValue(_langInput, l[1]);
87823               utilGetSetValue(_titleInput, m[2] + (anchor ? ('#' + anchor) : ''));
87824               if (anchor) {
87825                 try {
87826                   // Best-effort `anchorencode:` implementation
87827                   anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
87828                 } catch (e) {
87829                   anchor = anchor.replace(/ /g, '_');
87830                 }
87831               }
87832               _wikiURL = 'https://' + m[1] + '.wikipedia.org/wiki/' +
87833                 m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : '');
87834
87835             // unrecognized value format
87836             } else {
87837               utilGetSetValue(_titleInput, value);
87838               if (value && value !== '') {
87839                 utilGetSetValue(_langInput, '');
87840                 _wikiURL = `https://en.wikipedia.org/wiki/Special:Search?search=${value}`;
87841               } else {
87842                 _wikiURL = '';
87843               }
87844             }
87845           }
87846
87847
87848           wiki.entityIDs = (val) => {
87849             if (!arguments.length) return _entityIDs;
87850             _entityIDs = val;
87851             return wiki;
87852           };
87853
87854
87855           wiki.focus = () => {
87856             _titleInput.node().focus();
87857           };
87858
87859
87860           return utilRebind(wiki, dispatch$1, 'on');
87861         }
87862
87863         uiFieldWikipedia.supportsMultiselection = false;
87864
87865         var uiFields = {
87866             access: uiFieldAccess,
87867             address: uiFieldAddress,
87868             check: uiFieldCheck,
87869             combo: uiFieldCombo,
87870             cycleway: uiFieldCycleway,
87871             defaultCheck: uiFieldCheck,
87872             email: uiFieldText,
87873             identifier: uiFieldText,
87874             lanes: uiFieldLanes,
87875             localized: uiFieldLocalized,
87876             maxspeed: uiFieldMaxspeed,
87877             multiCombo: uiFieldCombo,
87878             networkCombo: uiFieldCombo,
87879             number: uiFieldText,
87880             onewayCheck: uiFieldCheck,
87881             radio: uiFieldRadio,
87882             restrictions: uiFieldRestrictions,
87883             semiCombo: uiFieldCombo,
87884             structureRadio: uiFieldRadio,
87885             tel: uiFieldText,
87886             text: uiFieldText,
87887             textarea: uiFieldTextarea,
87888             typeCombo: uiFieldCombo,
87889             url: uiFieldText,
87890             wikidata: uiFieldWikidata,
87891             wikipedia: uiFieldWikipedia
87892         };
87893
87894         function uiField(context, presetField, entityIDs, options) {
87895             options = Object.assign({
87896                 show: true,
87897                 wrap: true,
87898                 remove: true,
87899                 revert: true,
87900                 info: true
87901             }, options);
87902
87903             var dispatch$1 = dispatch('change', 'revert');
87904             var field = Object.assign({}, presetField);   // shallow copy
87905             field.domId = utilUniqueDomId('form-field-' + field.safeid);
87906             var _show = options.show;
87907             var _state = '';
87908             var _tags = {};
87909
87910             var _locked = false;
87911             var _lockedTip = uiTooltip()
87912                 .title(_t('inspector.lock.suggestion', { label: field.label }))
87913                 .placement('bottom');
87914
87915
87916             field.keys = field.keys || [field.key];
87917
87918             // only create the fields that are actually being shown
87919             if (_show && !field.impl) {
87920                 createField();
87921             }
87922
87923             // Creates the field.. This is done lazily,
87924             // once we know that the field will be shown.
87925             function createField() {
87926                 field.impl = uiFields[field.type](field, context)
87927                     .on('change', function(t, onInput) {
87928                         dispatch$1.call('change', field, t, onInput);
87929                     });
87930
87931                 if (entityIDs) {
87932                     field.entityIDs = entityIDs;
87933                     // if this field cares about the entities, pass them along
87934                     if (field.impl.entityIDs) {
87935                         field.impl.entityIDs(entityIDs);
87936                     }
87937                 }
87938             }
87939
87940
87941             function isModified() {
87942                 if (!entityIDs || !entityIDs.length) return false;
87943                 return entityIDs.some(function(entityID) {
87944                     var original = context.graph().base().entities[entityID];
87945                     var latest = context.graph().entity(entityID);
87946                     return field.keys.some(function(key) {
87947                         return original ? latest.tags[key] !== original.tags[key] : latest.tags[key];
87948                     });
87949                 });
87950             }
87951
87952
87953             function tagsContainFieldKey() {
87954                 return field.keys.some(function(key) {
87955                     if (field.type === 'multiCombo') {
87956                         for (var tagKey in _tags) {
87957                             if (tagKey.indexOf(key) === 0) {
87958                                 return true;
87959                             }
87960                         }
87961                         return false;
87962                     }
87963                     return _tags[key] !== undefined;
87964                 });
87965             }
87966
87967
87968             function revert(d) {
87969                 event.stopPropagation();
87970                 event.preventDefault();
87971                 if (!entityIDs || _locked) return;
87972
87973                 dispatch$1.call('revert', d, d.keys);
87974             }
87975
87976
87977             function remove(d) {
87978                 event.stopPropagation();
87979                 event.preventDefault();
87980                 if (_locked) return;
87981
87982                 var t = {};
87983                 d.keys.forEach(function(key) {
87984                     t[key] = undefined;
87985                 });
87986
87987                 dispatch$1.call('change', d, t);
87988             }
87989
87990
87991             field.render = function(selection) {
87992                 var container = selection.selectAll('.form-field')
87993                     .data([field]);
87994
87995                 // Enter
87996                 var enter = container.enter()
87997                     .append('div')
87998                     .attr('class', function(d) { return 'form-field form-field-' + d.safeid; })
87999                     .classed('nowrap', !options.wrap);
88000
88001                 if (options.wrap) {
88002                     var labelEnter = enter
88003                         .append('label')
88004                         .attr('class', 'field-label')
88005                         .attr('for', function(d) { return d.domId; });
88006
88007                     var textEnter = labelEnter
88008                         .append('span')
88009                         .attr('class', 'label-text');
88010
88011                     textEnter
88012                         .append('span')
88013                         .attr('class', 'label-textvalue')
88014                         .text(function(d) { return d.label(); });
88015
88016                     textEnter
88017                         .append('span')
88018                         .attr('class', 'label-textannotation');
88019
88020                     if (options.remove) {
88021                         labelEnter
88022                             .append('button')
88023                             .attr('class', 'remove-icon')
88024                             .attr('title', _t('icons.remove'))
88025                             .attr('tabindex', -1)
88026                             .call(svgIcon('#iD-operation-delete'));
88027                     }
88028
88029                     if (options.revert) {
88030                         labelEnter
88031                             .append('button')
88032                             .attr('class', 'modified-icon')
88033                             .attr('title', _t('icons.undo'))
88034                             .attr('tabindex', -1)
88035                             .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-redo' : '#iD-icon-undo'));
88036                     }
88037                 }
88038
88039
88040                 // Update
88041                 container = container
88042                     .merge(enter);
88043
88044                 container.select('.field-label > .remove-icon')  // propagate bound data
88045                     .on('click', remove);
88046
88047                 container.select('.field-label > .modified-icon')  // propagate bound data
88048                     .on('click', revert);
88049
88050                 container
88051                     .each(function(d) {
88052                         var selection = select(this);
88053
88054                         if (!d.impl) {
88055                             createField();
88056                         }
88057
88058                         var reference, help;
88059
88060                         // instantiate field help
88061                         if (options.wrap && field.type === 'restrictions') {
88062                             help = uiFieldHelp(context, 'restrictions');
88063                         }
88064
88065                         // instantiate tag reference
88066                         if (options.wrap && options.info) {
88067                             var referenceKey = d.key;
88068                             if (d.type === 'multiCombo') {   // lookup key without the trailing ':'
88069                                 referenceKey = referenceKey.replace(/:$/, '');
88070                             }
88071
88072                             reference = uiTagReference(d.reference || { key: referenceKey });
88073                             if (_state === 'hover') {
88074                                 reference.showing(false);
88075                             }
88076                         }
88077
88078                         selection
88079                             .call(d.impl);
88080
88081                         // add field help components
88082                         if (help) {
88083                             selection
88084                                 .call(help.body)
88085                                 .select('.field-label')
88086                                 .call(help.button);
88087                         }
88088
88089                         // add tag reference components
88090                         if (reference) {
88091                             selection
88092                                 .call(reference.body)
88093                                 .select('.field-label')
88094                                 .call(reference.button);
88095                         }
88096
88097                         d.impl.tags(_tags);
88098                     });
88099
88100
88101                     container
88102                         .classed('locked', _locked)
88103                         .classed('modified', isModified())
88104                         .classed('present', tagsContainFieldKey());
88105
88106
88107                     // show a tip and lock icon if the field is locked
88108                     var annotation = container.selectAll('.field-label .label-textannotation');
88109                     var icon = annotation.selectAll('.icon')
88110                         .data(_locked ? [0]: []);
88111
88112                     icon.exit()
88113                         .remove();
88114
88115                     icon.enter()
88116                         .append('svg')
88117                         .attr('class', 'icon')
88118                         .append('use')
88119                         .attr('xlink:href', '#fas-lock');
88120
88121                     container.call(_locked ? _lockedTip : _lockedTip.destroy);
88122             };
88123
88124
88125             field.state = function(val) {
88126                 if (!arguments.length) return _state;
88127                 _state = val;
88128                 return field;
88129             };
88130
88131
88132             field.tags = function(val) {
88133                 if (!arguments.length) return _tags;
88134                 _tags = val;
88135
88136                 if (tagsContainFieldKey() && !_show) {
88137                     // always show a field if it has a value to display
88138                     _show = true;
88139                     if (!field.impl) {
88140                         createField();
88141                     }
88142                 }
88143
88144                 return field;
88145             };
88146
88147
88148             field.locked = function(val) {
88149                 if (!arguments.length) return _locked;
88150                 _locked = val;
88151                 return field;
88152             };
88153
88154
88155             field.show = function() {
88156                 _show = true;
88157                 if (!field.impl) {
88158                     createField();
88159                 }
88160                 if (field.default && field.key && _tags[field.key] !== field.default) {
88161                     var t = {};
88162                     t[field.key] = field.default;
88163                     dispatch$1.call('change', this, t);
88164                 }
88165             };
88166
88167             // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown
88168             field.isShown = function() {
88169                 return _show;
88170             };
88171
88172
88173             // An allowed field can appear in the UI or in the 'Add field' dropdown.
88174             // A non-allowed field is hidden from the user altogether
88175             field.isAllowed = function() {
88176
88177                 if (entityIDs &&
88178                     entityIDs.length > 1 &&
88179                     uiFields[field.type].supportsMultiselection === false) return false;
88180
88181                 if (field.geometry && !entityIDs.every(function(entityID) {
88182                     return field.matchGeometry(context.graph().geometry(entityID));
88183                 })) return false;
88184
88185                 if (field.countryCodes || field.notCountryCodes) {
88186                     var extent = combinedEntityExtent();
88187                     if (!extent) return true;
88188
88189                     var center = extent.center();
88190                     var countryCode = iso1A2Code(center);
88191
88192                     if (!countryCode) return false;
88193
88194                     countryCode = countryCode.toLowerCase();
88195
88196                     if (field.countryCodes && field.countryCodes.indexOf(countryCode) === -1) {
88197                         return false;
88198                     }
88199                     if (field.notCountryCodes && field.notCountryCodes.indexOf(countryCode) !== -1) {
88200                         return false;
88201                     }
88202                 }
88203
88204                 var prerequisiteTag = field.prerequisiteTag;
88205
88206                 if (entityIDs &&
88207                     !tagsContainFieldKey() && // ignore tagging prerequisites if a value is already present
88208                     prerequisiteTag) {
88209
88210                     if (!entityIDs.every(function(entityID) {
88211                         var entity = context.graph().entity(entityID);
88212                         if (prerequisiteTag.key) {
88213                             var value = entity.tags[prerequisiteTag.key];
88214                             if (!value) return false;
88215
88216                             if (prerequisiteTag.valueNot) {
88217                                 return prerequisiteTag.valueNot !== value;
88218                             }
88219                             if (prerequisiteTag.value) {
88220                                 return prerequisiteTag.value === value;
88221                             }
88222                         } else if (prerequisiteTag.keyNot) {
88223                             if (entity.tags[prerequisiteTag.keyNot]) return false;
88224                         }
88225                         return true;
88226                     })) return false;
88227                 }
88228
88229                 return true;
88230             };
88231
88232
88233             field.focus = function() {
88234                 if (field.impl) {
88235                     field.impl.focus();
88236                 }
88237             };
88238
88239
88240             function combinedEntityExtent() {
88241                 return entityIDs && entityIDs.length && entityIDs.reduce(function(extent, entityID) {
88242                     var entity = context.graph().entity(entityID);
88243                     return extent.extend(entity.extent(context.graph()));
88244                 }, geoExtent());
88245             }
88246
88247
88248             return utilRebind(field, dispatch$1, 'on');
88249         }
88250
88251         function uiFormFields(context) {
88252             var moreCombo = uiCombobox(context, 'more-fields').minItems(1);
88253             var _fieldsArr = [];
88254             var _lastPlaceholder = '';
88255             var _state = '';
88256             var _klass = '';
88257
88258
88259             function formFields(selection) {
88260                 var allowedFields = _fieldsArr.filter(function(field) { return field.isAllowed(); });
88261                 var shown = allowedFields.filter(function(field) { return field.isShown(); });
88262                 var notShown = allowedFields.filter(function(field) { return !field.isShown(); });
88263
88264                 var container = selection.selectAll('.form-fields-container')
88265                     .data([0]);
88266
88267                 container = container.enter()
88268                     .append('div')
88269                     .attr('class', 'form-fields-container ' + (_klass || ''))
88270                     .merge(container);
88271
88272
88273                 var fields = container.selectAll('.wrap-form-field')
88274                     .data(shown, function(d) { return d.id + (d.entityIDs ? d.entityIDs.join() : ''); });
88275
88276                 fields.exit()
88277                     .remove();
88278
88279                 // Enter
88280                 var enter = fields.enter()
88281                     .append('div')
88282                     .attr('class', function(d) { return 'wrap-form-field wrap-form-field-' + d.safeid; });
88283
88284                 // Update
88285                 fields = fields
88286                     .merge(enter);
88287
88288                 fields
88289                     .order()
88290                     .each(function(d) {
88291                         select(this)
88292                             .call(d.render);
88293                     });
88294
88295
88296                 var titles = [];
88297                 var moreFields = notShown.map(function(field) {
88298                     var label = field.label();
88299                     titles.push(label);
88300
88301                     var terms = field.terms();
88302                     if (field.key) terms.push(field.key);
88303                     if (field.keys) terms = terms.concat(field.keys);
88304
88305                     return {
88306                         title: label,
88307                         value: label,
88308                         field: field,
88309                         terms: terms
88310                     };
88311                 });
88312
88313                 var placeholder = titles.slice(0,3).join(', ') + ((titles.length > 3) ? '…' : '');
88314
88315
88316                 var more = selection.selectAll('.more-fields')
88317                     .data((_state === 'hover' || moreFields.length === 0) ? [] : [0]);
88318
88319                 more.exit()
88320                     .remove();
88321
88322                 var moreEnter = more.enter()
88323                     .append('div')
88324                     .attr('class', 'more-fields')
88325                     .append('label');
88326
88327                 moreEnter
88328                     .append('span')
88329                     .text(_t('inspector.add_fields'));
88330
88331                 more = moreEnter
88332                     .merge(more);
88333
88334
88335                 var input = more.selectAll('.value')
88336                     .data([0]);
88337
88338                 input.exit()
88339                     .remove();
88340
88341                 input = input.enter()
88342                     .append('input')
88343                     .attr('class', 'value')
88344                     .attr('type', 'text')
88345                     .attr('placeholder', placeholder)
88346                     .call(utilNoAuto)
88347                     .merge(input);
88348
88349                 input
88350                     .call(utilGetSetValue, '')
88351                     .call(moreCombo
88352                         .data(moreFields)
88353                         .on('accept', function (d) {
88354                             if (!d) return;  // user entered something that was not matched
88355                             var field = d.field;
88356                             field.show();
88357                             selection.call(formFields);  // rerender
88358                             if (field.type !== 'semiCombo' && field.type !== 'multiCombo') {
88359                                 field.focus();
88360                             }
88361                         })
88362                     );
88363
88364                 // avoid updating placeholder excessively (triggers style recalc)
88365                 if (_lastPlaceholder !== placeholder) {
88366                     input.attr('placeholder', placeholder);
88367                     _lastPlaceholder = placeholder;
88368                 }
88369             }
88370
88371
88372             formFields.fieldsArr = function(val) {
88373                 if (!arguments.length) return _fieldsArr;
88374                 _fieldsArr = val || [];
88375                 return formFields;
88376             };
88377
88378             formFields.state = function(val) {
88379                 if (!arguments.length) return _state;
88380                 _state = val;
88381                 return formFields;
88382             };
88383
88384             formFields.klass = function(val) {
88385                 if (!arguments.length) return _klass;
88386                 _klass = val;
88387                 return formFields;
88388             };
88389
88390
88391             return formFields;
88392         }
88393
88394         function uiSectionPresetFields(context) {
88395
88396             var section = uiSection('preset-fields', context)
88397                 .title(function() {
88398                     return _t('inspector.fields');
88399                 })
88400                 .disclosureContent(renderDisclosureContent);
88401
88402             var dispatch$1 = dispatch('change', 'revert');
88403             var formFields = uiFormFields(context);
88404             var _state;
88405             var _fieldsArr;
88406             var _presets = [];
88407             var _tags;
88408             var _entityIDs;
88409
88410             function renderDisclosureContent(selection) {
88411                 if (!_fieldsArr) {
88412
88413                     var graph = context.graph();
88414
88415                     var geometries = Object.keys(_entityIDs.reduce(function(geoms, entityID) {
88416                         return geoms[graph.entity(entityID).geometry(graph)] = true;
88417                     }, {}));
88418
88419                     var presetsManager = _mainPresetIndex;
88420
88421                     var allFields = [];
88422                     var allMoreFields = [];
88423                     var sharedTotalFields;
88424
88425                     _presets.forEach(function(preset) {
88426                         var fields = preset.fields();
88427                         var moreFields = preset.moreFields();
88428
88429                         allFields = utilArrayUnion(allFields, fields);
88430                         allMoreFields = utilArrayUnion(allMoreFields, moreFields);
88431
88432                         if (!sharedTotalFields) {
88433                             sharedTotalFields = utilArrayUnion(fields, moreFields);
88434                         } else {
88435                             sharedTotalFields = sharedTotalFields.filter(function(field) {
88436                                 return fields.indexOf(field) !== -1 || moreFields.indexOf(field) !== -1;
88437                             });
88438                         }
88439                     });
88440
88441                     var sharedFields = allFields.filter(function(field) {
88442                         return sharedTotalFields.indexOf(field) !== -1;
88443                     });
88444                     var sharedMoreFields = allMoreFields.filter(function(field) {
88445                         return sharedTotalFields.indexOf(field) !== -1;
88446                     });
88447
88448                     _fieldsArr = [];
88449
88450                     sharedFields.forEach(function(field) {
88451                         if (field.matchAllGeometry(geometries)) {
88452                             _fieldsArr.push(
88453                                 uiField(context, field, _entityIDs)
88454                             );
88455                         }
88456                     });
88457
88458                     var singularEntity = _entityIDs.length === 1 && graph.hasEntity(_entityIDs[0]);
88459                     if (singularEntity && singularEntity.isHighwayIntersection(graph) && presetsManager.field('restrictions')) {
88460                         _fieldsArr.push(
88461                             uiField(context, presetsManager.field('restrictions'), _entityIDs)
88462                         );
88463                     }
88464
88465                     var additionalFields = utilArrayUnion(sharedMoreFields, presetsManager.universal());
88466                     additionalFields.sort(function(field1, field2) {
88467                         return field1.label().localeCompare(field2.label(), _mainLocalizer.localeCode());
88468                     });
88469
88470                     additionalFields.forEach(function(field) {
88471                         if (sharedFields.indexOf(field) === -1 &&
88472                             field.matchAllGeometry(geometries)) {
88473                             _fieldsArr.push(
88474                                 uiField(context, field, _entityIDs, { show: false })
88475                             );
88476                         }
88477                     });
88478
88479                     _fieldsArr.forEach(function(field) {
88480                         field
88481                             .on('change', function(t, onInput) {
88482                                 dispatch$1.call('change', field, _entityIDs, t, onInput);
88483                             })
88484                             .on('revert', function(keys) {
88485                                 dispatch$1.call('revert', field, keys);
88486                             });
88487                     });
88488                 }
88489
88490                 _fieldsArr.forEach(function(field) {
88491                     field
88492                         .state(_state)
88493                         .tags(_tags);
88494                 });
88495
88496
88497                 selection
88498                     .call(formFields
88499                         .fieldsArr(_fieldsArr)
88500                         .state(_state)
88501                         .klass('grouped-items-area')
88502                     );
88503
88504
88505                 selection.selectAll('.wrap-form-field input')
88506                     .on('keydown', function() {
88507                         // if user presses enter, and combobox is not active, accept edits..
88508                         if (event.keyCode === 13 && context.container().select('.combobox').empty()) {
88509                             context.enter(modeBrowse(context));
88510                         }
88511                     });
88512             }
88513
88514             section.presets = function(val) {
88515                 if (!arguments.length) return _presets;
88516                 if (!_presets || !val || !utilArrayIdentical(_presets, val)) {
88517                     _presets = val;
88518                     _fieldsArr = null;
88519                 }
88520                 return section;
88521             };
88522
88523             section.state = function(val) {
88524                 if (!arguments.length) return _state;
88525                 _state = val;
88526                 return section;
88527             };
88528
88529             section.tags = function(val) {
88530                 if (!arguments.length) return _tags;
88531                 _tags = val;
88532                 // Don't reset _fieldsArr here.
88533                 return section;
88534             };
88535
88536             section.entityIDs = function(val) {
88537                 if (!arguments.length) return _entityIDs;
88538                 if (!val || !_entityIDs || !utilArrayIdentical(_entityIDs, val)) {
88539                     _entityIDs = val;
88540                     _fieldsArr = null;
88541                 }
88542                 return section;
88543             };
88544
88545             return utilRebind(section, dispatch$1, 'on');
88546         }
88547
88548         function uiSectionRawMemberEditor(context) {
88549
88550             var section = uiSection('raw-member-editor', context)
88551                 .shouldDisplay(function() {
88552                     if (!_entityIDs || _entityIDs.length !== 1) return false;
88553
88554                     var entity = context.hasEntity(_entityIDs[0]);
88555                     return entity && entity.type === 'relation';
88556                 })
88557                 .title(function() {
88558                     var entity = context.hasEntity(_entityIDs[0]);
88559                     if (!entity) return '';
88560
88561                     var gt = entity.members.length > _maxMembers ? '>' : '';
88562                     var count = gt + entity.members.slice(0, _maxMembers).length;
88563                     return _t('inspector.title_count', { title: _t('inspector.members'), count: count });
88564                 })
88565                 .disclosureContent(renderDisclosureContent);
88566
88567             var taginfo = services.taginfo;
88568             var _entityIDs;
88569             var _maxMembers = 1000;
88570
88571             function downloadMember(d) {
88572                 event.preventDefault();
88573
88574                 // display the loading indicator
88575                 select(this.parentNode).classed('tag-reference-loading', true);
88576                 context.loadEntity(d.id, function() {
88577                     section.reRender();
88578                 });
88579             }
88580
88581             function zoomToMember(d) {
88582                 event.preventDefault();
88583
88584                 var entity = context.entity(d.id);
88585                 context.map().zoomToEase(entity);
88586
88587                 // highlight the feature in case it wasn't previously on-screen
88588                 utilHighlightEntities([d.id], true, context);
88589             }
88590
88591
88592             function selectMember(d) {
88593                 event.preventDefault();
88594
88595                 // remove the hover-highlight styling
88596                 utilHighlightEntities([d.id], false, context);
88597
88598                 var entity = context.entity(d.id);
88599                 var mapExtent = context.map().extent();
88600                 if (!entity.intersects(mapExtent, context.graph())) {
88601                     // zoom to the entity if its extent is not visible now
88602                     context.map().zoomToEase(entity);
88603                 }
88604
88605                 context.enter(modeSelect(context, [d.id]));
88606             }
88607
88608
88609             function changeRole(d) {
88610                 var oldRole = d.role;
88611                 var newRole = context.cleanRelationRole(select(this).property('value'));
88612
88613                 if (oldRole !== newRole) {
88614                     var member = { id: d.id, type: d.type, role: newRole };
88615                     context.perform(
88616                         actionChangeMember(d.relation.id, member, d.index),
88617                         _t('operations.change_role.annotation')
88618                     );
88619                 }
88620             }
88621
88622
88623             function deleteMember(d) {
88624
88625                 // remove the hover-highlight styling
88626                 utilHighlightEntities([d.id], false, context);
88627
88628                 context.perform(
88629                     actionDeleteMember(d.relation.id, d.index),
88630                     _t('operations.delete_member.annotation')
88631                 );
88632
88633                 if (!context.hasEntity(d.relation.id)) {
88634                     context.enter(modeBrowse(context));
88635                 }
88636             }
88637
88638             function renderDisclosureContent(selection) {
88639
88640                 var entityID = _entityIDs[0];
88641
88642                 var memberships = [];
88643                 var entity = context.entity(entityID);
88644                 entity.members.slice(0, _maxMembers).forEach(function(member, index) {
88645                     memberships.push({
88646                         index: index,
88647                         id: member.id,
88648                         type: member.type,
88649                         role: member.role,
88650                         relation: entity,
88651                         member: context.hasEntity(member.id),
88652                         domId: utilUniqueDomId(entityID + '-member-' + index)
88653                     });
88654                 });
88655
88656                 var list = selection.selectAll('.member-list')
88657                     .data([0]);
88658
88659                 list = list.enter()
88660                     .append('ul')
88661                     .attr('class', 'member-list')
88662                     .merge(list);
88663
88664
88665                 var items = list.selectAll('li')
88666                     .data(memberships, function(d) {
88667                         return osmEntity.key(d.relation) + ',' + d.index + ',' +
88668                             (d.member ? osmEntity.key(d.member) : 'incomplete');
88669                     });
88670
88671                 items.exit()
88672                     .each(unbind)
88673                     .remove();
88674
88675                 var itemsEnter = items.enter()
88676                     .append('li')
88677                     .attr('class', 'member-row form-field')
88678                     .classed('member-incomplete', function(d) { return !d.member; });
88679
88680                 itemsEnter
88681                     .each(function(d) {
88682                         var item = select(this);
88683
88684                         var label = item
88685                             .append('label')
88686                             .attr('class', 'field-label')
88687                             .attr('for', d.domId);
88688
88689                         if (d.member) {
88690                             // highlight the member feature in the map while hovering on the list item
88691                             item
88692                                 .on('mouseover', function() {
88693                                     utilHighlightEntities([d.id], true, context);
88694                                 })
88695                                 .on('mouseout', function() {
88696                                     utilHighlightEntities([d.id], false, context);
88697                                 });
88698
88699                             var labelLink = label
88700                                 .append('span')
88701                                 .attr('class', 'label-text')
88702                                 .append('a')
88703                                 .attr('href', '#')
88704                                 .on('click', selectMember);
88705
88706                             labelLink
88707                                 .append('span')
88708                                 .attr('class', 'member-entity-type')
88709                                 .text(function(d) {
88710                                     var matched = _mainPresetIndex.match(d.member, context.graph());
88711                                     return (matched && matched.name()) || utilDisplayType(d.member.id);
88712                                 });
88713
88714                             labelLink
88715                                 .append('span')
88716                                 .attr('class', 'member-entity-name')
88717                                 .text(function(d) { return utilDisplayName(d.member); });
88718
88719                             label
88720                                 .append('button')
88721                                 .attr('tabindex', -1)
88722                                 .attr('title', _t('icons.remove'))
88723                                 .attr('class', 'remove member-delete')
88724                                 .call(svgIcon('#iD-operation-delete'));
88725
88726                             label
88727                                 .append('button')
88728                                 .attr('class', 'member-zoom')
88729                                 .attr('title', _t('icons.zoom_to'))
88730                                 .call(svgIcon('#iD-icon-framed-dot', 'monochrome'))
88731                                 .on('click', zoomToMember);
88732
88733                         } else {
88734                             var labelText = label
88735                                 .append('span')
88736                                 .attr('class', 'label-text');
88737
88738                             labelText
88739                                 .append('span')
88740                                 .attr('class', 'member-entity-type')
88741                                 .text(_t('inspector.' + d.type, { id: d.id }));
88742
88743                             labelText
88744                                 .append('span')
88745                                 .attr('class', 'member-entity-name')
88746                                 .text(_t('inspector.incomplete', { id: d.id }));
88747
88748                             label
88749                                 .append('button')
88750                                 .attr('class', 'member-download')
88751                                 .attr('title', _t('icons.download'))
88752                                 .attr('tabindex', -1)
88753                                 .call(svgIcon('#iD-icon-load'))
88754                                 .on('click', downloadMember);
88755                         }
88756                     });
88757
88758                 var wrapEnter = itemsEnter
88759                     .append('div')
88760                     .attr('class', 'form-field-input-wrap form-field-input-member');
88761
88762                 wrapEnter
88763                     .append('input')
88764                     .attr('class', 'member-role')
88765                     .attr('id', function(d) {
88766                         return d.domId;
88767                     })
88768                     .property('type', 'text')
88769                     .attr('placeholder', _t('inspector.role'))
88770                     .call(utilNoAuto);
88771
88772                 if (taginfo) {
88773                     wrapEnter.each(bindTypeahead);
88774                 }
88775
88776                 // update
88777                 items = items
88778                     .merge(itemsEnter)
88779                     .order();
88780
88781                 items.select('input.member-role')
88782                     .property('value', function(d) { return d.role; })
88783                     .on('blur', changeRole)
88784                     .on('change', changeRole);
88785
88786                 items.select('button.member-delete')
88787                     .on('click', deleteMember);
88788
88789                 var dragOrigin, targetIndex;
88790
88791                 items.call(d3_drag()
88792                     .on('start', function() {
88793                         dragOrigin = {
88794                             x: event.x,
88795                             y: event.y
88796                         };
88797                         targetIndex = null;
88798                     })
88799                     .on('drag', function(d, index) {
88800                         var x = event.x - dragOrigin.x,
88801                             y = event.y - dragOrigin.y;
88802
88803                         if (!select(this).classed('dragging') &&
88804                             // don't display drag until dragging beyond a distance threshold
88805                             Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
88806
88807                         select(this)
88808                             .classed('dragging', true);
88809
88810                         targetIndex = null;
88811
88812                         selection.selectAll('li.member-row')
88813                             .style('transform', function(d2, index2) {
88814                                 var node = select(this).node();
88815                                 if (index === index2) {
88816                                     return 'translate(' + x + 'px, ' + y + 'px)';
88817                                 } else if (index2 > index && event.y > node.offsetTop) {
88818                                     if (targetIndex === null || index2 > targetIndex) {
88819                                         targetIndex = index2;
88820                                     }
88821                                     return 'translateY(-100%)';
88822                                 } else if (index2 < index && event.y < node.offsetTop + node.offsetHeight) {
88823                                     if (targetIndex === null || index2 < targetIndex) {
88824                                         targetIndex = index2;
88825                                     }
88826                                     return 'translateY(100%)';
88827                                 }
88828                                 return null;
88829                             });
88830                     })
88831                     .on('end', function(d, index) {
88832
88833                         if (!select(this).classed('dragging')) {
88834                             return;
88835                         }
88836
88837                         select(this)
88838                             .classed('dragging', false);
88839
88840                         selection.selectAll('li.member-row')
88841                             .style('transform', null);
88842
88843                         if (targetIndex !== null) {
88844                             // dragged to a new position, reorder
88845                             context.perform(
88846                                 actionMoveMember(d.relation.id, index, targetIndex),
88847                                 _t('operations.reorder_members.annotation')
88848                             );
88849                         }
88850                     })
88851                 );
88852
88853
88854
88855                 function bindTypeahead(d) {
88856                     var row = select(this);
88857                     var role = row.selectAll('input.member-role');
88858                     var origValue = role.property('value');
88859
88860                     function sort(value, data) {
88861                         var sameletter = [];
88862                         var other = [];
88863                         for (var i = 0; i < data.length; i++) {
88864                             if (data[i].value.substring(0, value.length) === value) {
88865                                 sameletter.push(data[i]);
88866                             } else {
88867                                 other.push(data[i]);
88868                             }
88869                         }
88870                         return sameletter.concat(other);
88871                     }
88872
88873                     role.call(uiCombobox(context, 'member-role')
88874                         .fetcher(function(role, callback) {
88875                             // The `geometry` param is used in the `taginfo.js` interface for
88876                             // filtering results, as a key into the `tag_members_fractions`
88877                             // object.  If we don't know the geometry because the member is
88878                             // not yet downloaded, it's ok to guess based on type.
88879                             var geometry;
88880                             if (d.member) {
88881                                 geometry = context.graph().geometry(d.member.id);
88882                             } else if (d.type === 'relation') {
88883                                 geometry = 'relation';
88884                             } else if (d.type === 'way') {
88885                                 geometry = 'line';
88886                             } else {
88887                                 geometry = 'point';
88888                             }
88889
88890                             var rtype = entity.tags.type;
88891                             taginfo.roles({
88892                                 debounce: true,
88893                                 rtype: rtype || '',
88894                                 geometry: geometry,
88895                                 query: role
88896                             }, function(err, data) {
88897                                 if (!err) callback(sort(role, data));
88898                             });
88899                         })
88900                         .on('cancel', function() {
88901                             role.property('value', origValue);
88902                         })
88903                     );
88904                 }
88905
88906
88907                 function unbind() {
88908                     var row = select(this);
88909
88910                     row.selectAll('input.member-role')
88911                         .call(uiCombobox.off, context);
88912                 }
88913             }
88914
88915             section.entityIDs = function(val) {
88916                 if (!arguments.length) return _entityIDs;
88917                 _entityIDs = val;
88918                 return section;
88919             };
88920
88921
88922             return section;
88923         }
88924
88925         function uiSectionRawMembershipEditor(context) {
88926
88927             var section = uiSection('raw-membership-editor', context)
88928                 .shouldDisplay(function() {
88929                     return _entityIDs && _entityIDs.length === 1;
88930                 })
88931                 .title(function() {
88932                     var entity = context.hasEntity(_entityIDs[0]);
88933                     if (!entity) return '';
88934
88935                     var parents = context.graph().parentRelations(entity);
88936                     var gt = parents.length > _maxMemberships ? '>' : '';
88937                     var count = gt + parents.slice(0, _maxMemberships).length;
88938                     return _t('inspector.title_count', { title: _t('inspector.relations'), count: count });
88939                 })
88940                 .disclosureContent(renderDisclosureContent);
88941
88942             var taginfo = services.taginfo;
88943             var nearbyCombo = uiCombobox(context, 'parent-relation')
88944                 .minItems(1)
88945                 .fetcher(fetchNearbyRelations)
88946                 .itemsMouseEnter(function(d) {
88947                     if (d.relation) utilHighlightEntities([d.relation.id], true, context);
88948                 })
88949                 .itemsMouseLeave(function(d) {
88950                     if (d.relation) utilHighlightEntities([d.relation.id], false, context);
88951                 });
88952             var _inChange = false;
88953             var _entityIDs = [];
88954             var _showBlank;
88955             var _maxMemberships = 1000;
88956
88957             function selectRelation(d) {
88958                 event.preventDefault();
88959
88960                 // remove the hover-highlight styling
88961                 utilHighlightEntities([d.relation.id], false, context);
88962
88963                 context.enter(modeSelect(context, [d.relation.id]));
88964             }
88965
88966             function zoomToRelation(d) {
88967                 event.preventDefault();
88968
88969                 var entity = context.entity(d.relation.id);
88970                 context.map().zoomToEase(entity);
88971
88972                 // highlight the relation in case it wasn't previously on-screen
88973                 utilHighlightEntities([d.relation.id], true, context);
88974             }
88975
88976
88977             function changeRole(d) {
88978                 if (d === 0) return;    // called on newrow (shoudn't happen)
88979                 if (_inChange) return;  // avoid accidental recursive call #5731
88980
88981                 var oldRole = d.member.role;
88982                 var newRole = context.cleanRelationRole(select(this).property('value'));
88983
88984                 if (oldRole !== newRole) {
88985                     _inChange = true;
88986                     context.perform(
88987                         actionChangeMember(d.relation.id, Object.assign({}, d.member, { role: newRole }), d.index),
88988                         _t('operations.change_role.annotation')
88989                     );
88990                 }
88991                 _inChange = false;
88992             }
88993
88994
88995             function addMembership(d, role) {
88996                 this.blur();           // avoid keeping focus on the button
88997                 _showBlank = false;
88998
88999                 var member = { id: _entityIDs[0], type: context.entity(_entityIDs[0]).type, role: role };
89000
89001                 if (d.relation) {
89002                     context.perform(
89003                         actionAddMember(d.relation.id, member),
89004                         _t('operations.add_member.annotation')
89005                     );
89006
89007                 } else {
89008                     var relation = osmRelation();
89009                     context.perform(
89010                         actionAddEntity(relation),
89011                         actionAddMember(relation.id, member),
89012                         _t('operations.add.annotation.relation')
89013                     );
89014
89015                     context.enter(modeSelect(context, [relation.id]).newFeature(true));
89016                 }
89017             }
89018
89019
89020             function deleteMembership(d) {
89021                 this.blur();           // avoid keeping focus on the button
89022                 if (d === 0) return;   // called on newrow (shoudn't happen)
89023
89024                 // remove the hover-highlight styling
89025                 utilHighlightEntities([d.relation.id], false, context);
89026
89027                 context.perform(
89028                     actionDeleteMember(d.relation.id, d.index),
89029                     _t('operations.delete_member.annotation')
89030                 );
89031             }
89032
89033
89034             function fetchNearbyRelations(q, callback) {
89035                 var newRelation = { relation: null, value: _t('inspector.new_relation') };
89036
89037                 var entityID = _entityIDs[0];
89038
89039                 var result = [];
89040
89041                 var graph = context.graph();
89042
89043                 function baseDisplayLabel(entity) {
89044                     var matched = _mainPresetIndex.match(entity, graph);
89045                     var presetName = (matched && matched.name()) || _t('inspector.relation');
89046                     var entityName = utilDisplayName(entity) || '';
89047
89048                     return presetName + ' ' + entityName;
89049                 }
89050
89051                 var explicitRelation = q && context.hasEntity(q.toLowerCase());
89052                 if (explicitRelation && explicitRelation.type === 'relation' && explicitRelation.id !== entityID) {
89053                     // loaded relation is specified explicitly, only show that
89054
89055                     result.push({
89056                         relation: explicitRelation,
89057                         value: baseDisplayLabel(explicitRelation) + ' ' + explicitRelation.id
89058                     });
89059                 } else {
89060
89061                     context.history().intersects(context.map().extent()).forEach(function(entity) {
89062                         if (entity.type !== 'relation' || entity.id === entityID) return;
89063
89064                         var value = baseDisplayLabel(entity);
89065                         if (q && (value + ' ' + entity.id).toLowerCase().indexOf(q.toLowerCase()) === -1) return;
89066
89067                         result.push({ relation: entity, value: value });
89068                     });
89069
89070                     result.sort(function(a, b) {
89071                         return osmRelation.creationOrder(a.relation, b.relation);
89072                     });
89073
89074                     // Dedupe identical names by appending relation id - see #2891
89075                     var dupeGroups = Object.values(utilArrayGroupBy(result, 'value'))
89076                         .filter(function(v) { return v.length > 1; });
89077
89078                     dupeGroups.forEach(function(group) {
89079                         group.forEach(function(obj) {
89080                             obj.value += ' ' + obj.relation.id;
89081                         });
89082                     });
89083                 }
89084
89085                 result.forEach(function(obj) {
89086                     obj.title = obj.value;
89087                 });
89088
89089                 result.unshift(newRelation);
89090                 callback(result);
89091             }
89092
89093             function renderDisclosureContent(selection) {
89094
89095                 var entityID = _entityIDs[0];
89096
89097                 var entity = context.entity(entityID);
89098                 var parents = context.graph().parentRelations(entity);
89099
89100                 var memberships = [];
89101
89102                 parents.slice(0, _maxMemberships).forEach(function(relation) {
89103                     relation.members.forEach(function(member, index) {
89104                         if (member.id === entity.id) {
89105                             memberships.push({
89106                                 relation: relation,
89107                                 member: member,
89108                                 index: index,
89109                                 domId: utilUniqueDomId(entityID + '-membership-' + relation.id + '-' + index)
89110                             });
89111                         }
89112                     });
89113                 });
89114
89115                 var list = selection.selectAll('.member-list')
89116                     .data([0]);
89117
89118                 list = list.enter()
89119                     .append('ul')
89120                     .attr('class', 'member-list')
89121                     .merge(list);
89122
89123
89124                 var items = list.selectAll('li.member-row-normal')
89125                     .data(memberships, function(d) {
89126                         return osmEntity.key(d.relation) + ',' + d.index;
89127                     });
89128
89129                 items.exit()
89130                     .each(unbind)
89131                     .remove();
89132
89133                 // Enter
89134                 var itemsEnter = items.enter()
89135                     .append('li')
89136                     .attr('class', 'member-row member-row-normal form-field');
89137
89138                 // highlight the relation in the map while hovering on the list item
89139                 itemsEnter.on('mouseover', function(d) {
89140                         utilHighlightEntities([d.relation.id], true, context);
89141                     })
89142                     .on('mouseout', function(d) {
89143                         utilHighlightEntities([d.relation.id], false, context);
89144                     });
89145
89146                 var labelEnter = itemsEnter
89147                     .append('label')
89148                     .attr('class', 'field-label')
89149                     .attr('for', function(d) {
89150                         return d.domId;
89151                     });
89152
89153                 var labelLink = labelEnter
89154                     .append('span')
89155                     .attr('class', 'label-text')
89156                     .append('a')
89157                     .attr('href', '#')
89158                     .on('click', selectRelation);
89159
89160                 labelLink
89161                     .append('span')
89162                     .attr('class', 'member-entity-type')
89163                     .text(function(d) {
89164                         var matched = _mainPresetIndex.match(d.relation, context.graph());
89165                         return (matched && matched.name()) || _t('inspector.relation');
89166                     });
89167
89168                 labelLink
89169                     .append('span')
89170                     .attr('class', 'member-entity-name')
89171                     .text(function(d) { return utilDisplayName(d.relation); });
89172
89173                 labelEnter
89174                     .append('button')
89175                     .attr('tabindex', -1)
89176                     .attr('class', 'remove member-delete')
89177                     .call(svgIcon('#iD-operation-delete'))
89178                     .on('click', deleteMembership);
89179
89180                 labelEnter
89181                     .append('button')
89182                     .attr('class', 'member-zoom')
89183                     .attr('title', _t('icons.zoom_to'))
89184                     .call(svgIcon('#iD-icon-framed-dot', 'monochrome'))
89185                     .on('click', zoomToRelation);
89186
89187                 var wrapEnter = itemsEnter
89188                     .append('div')
89189                     .attr('class', 'form-field-input-wrap form-field-input-member');
89190
89191                 wrapEnter
89192                     .append('input')
89193                     .attr('class', 'member-role')
89194                     .attr('id', function(d) {
89195                         return d.domId;
89196                     })
89197                     .property('type', 'text')
89198                     .attr('placeholder', _t('inspector.role'))
89199                     .call(utilNoAuto)
89200                     .property('value', function(d) { return d.member.role; })
89201                     .on('blur', changeRole)
89202                     .on('change', changeRole);
89203
89204                 if (taginfo) {
89205                     wrapEnter.each(bindTypeahead);
89206                 }
89207
89208
89209                 var newMembership = list.selectAll('.member-row-new')
89210                     .data(_showBlank ? [0] : []);
89211
89212                 // Exit
89213                 newMembership.exit()
89214                     .remove();
89215
89216                 // Enter
89217                 var newMembershipEnter = newMembership.enter()
89218                     .append('li')
89219                     .attr('class', 'member-row member-row-new form-field');
89220
89221                 var newLabelEnter = newMembershipEnter
89222                     .append('label')
89223                     .attr('class', 'field-label');
89224
89225                 newLabelEnter
89226                     .append('input')
89227                     .attr('placeholder', _t('inspector.choose_relation'))
89228                     .attr('type', 'text')
89229                     .attr('class', 'member-entity-input')
89230                     .call(utilNoAuto);
89231
89232                 newLabelEnter
89233                     .append('button')
89234                     .attr('tabindex', -1)
89235                     .attr('class', 'remove member-delete')
89236                     .call(svgIcon('#iD-operation-delete'))
89237                     .on('click', function() {
89238                         list.selectAll('.member-row-new')
89239                             .remove();
89240                     });
89241
89242                 var newWrapEnter = newMembershipEnter
89243                     .append('div')
89244                     .attr('class', 'form-field-input-wrap form-field-input-member');
89245
89246                 newWrapEnter
89247                     .append('input')
89248                     .attr('class', 'member-role')
89249                     .property('type', 'text')
89250                     .attr('placeholder', _t('inspector.role'))
89251                     .call(utilNoAuto);
89252
89253                 // Update
89254                 newMembership = newMembership
89255                     .merge(newMembershipEnter);
89256
89257                 newMembership.selectAll('.member-entity-input')
89258                     .on('blur', cancelEntity)   // if it wasn't accepted normally, cancel it
89259                     .call(nearbyCombo
89260                         .on('accept', acceptEntity)
89261                         .on('cancel', cancelEntity)
89262                     );
89263
89264
89265                 // Container for the Add button
89266                 var addRow = selection.selectAll('.add-row')
89267                     .data([0]);
89268
89269                 // enter
89270                 var addRowEnter = addRow.enter()
89271                     .append('div')
89272                     .attr('class', 'add-row');
89273
89274                 var addRelationButton = addRowEnter
89275                     .append('button')
89276                     .attr('class', 'add-relation');
89277
89278                 addRelationButton
89279                     .call(svgIcon('#iD-icon-plus', 'light'));
89280                 addRelationButton
89281                     .call(uiTooltip().title(_t('inspector.add_to_relation')).placement(_mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left'));
89282
89283                 addRowEnter
89284                     .append('div')
89285                     .attr('class', 'space-value');   // preserve space
89286
89287                 addRowEnter
89288                     .append('div')
89289                     .attr('class', 'space-buttons');  // preserve space
89290
89291                 // update
89292                 addRow = addRow
89293                     .merge(addRowEnter);
89294
89295                 addRow.select('.add-relation')
89296                     .on('click', function() {
89297                         _showBlank = true;
89298                         section.reRender();
89299                         list.selectAll('.member-entity-input').node().focus();
89300                     });
89301
89302
89303                 function acceptEntity(d) {
89304                     if (!d) {
89305                         cancelEntity();
89306                         return;
89307                     }
89308                     // remove hover-higlighting
89309                     if (d.relation) utilHighlightEntities([d.relation.id], false, context);
89310
89311                     var role = context.cleanRelationRole(list.selectAll('.member-row-new .member-role').property('value'));
89312                     addMembership(d, role);
89313                 }
89314
89315
89316                 function cancelEntity() {
89317                     var input = newMembership.selectAll('.member-entity-input');
89318                     input.property('value', '');
89319
89320                     // remove hover-higlighting
89321                     context.surface().selectAll('.highlighted')
89322                         .classed('highlighted', false);
89323                 }
89324
89325
89326                 function bindTypeahead(d) {
89327                     var row = select(this);
89328                     var role = row.selectAll('input.member-role');
89329                     var origValue = role.property('value');
89330
89331                     function sort(value, data) {
89332                         var sameletter = [];
89333                         var other = [];
89334                         for (var i = 0; i < data.length; i++) {
89335                             if (data[i].value.substring(0, value.length) === value) {
89336                                 sameletter.push(data[i]);
89337                             } else {
89338                                 other.push(data[i]);
89339                             }
89340                         }
89341                         return sameletter.concat(other);
89342                     }
89343
89344                     role.call(uiCombobox(context, 'member-role')
89345                         .fetcher(function(role, callback) {
89346                             var rtype = d.relation.tags.type;
89347                             taginfo.roles({
89348                                 debounce: true,
89349                                 rtype: rtype || '',
89350                                 geometry: context.graph().geometry(entityID),
89351                                 query: role
89352                             }, function(err, data) {
89353                                 if (!err) callback(sort(role, data));
89354                             });
89355                         })
89356                         .on('cancel', function() {
89357                             role.property('value', origValue);
89358                         })
89359                     );
89360                 }
89361
89362
89363                 function unbind() {
89364                     var row = select(this);
89365
89366                     row.selectAll('input.member-role')
89367                         .call(uiCombobox.off, context);
89368                 }
89369             }
89370
89371
89372             section.entityIDs = function(val) {
89373                 if (!arguments.length) return _entityIDs;
89374                 _entityIDs = val;
89375                 _showBlank = false;
89376                 return section;
89377             };
89378
89379
89380             return section;
89381         }
89382
89383         function uiSectionSelectionList(context) {
89384
89385             var _selectedIDs = [];
89386
89387             var section = uiSection('selected-features', context)
89388                 .shouldDisplay(function() {
89389                     return _selectedIDs.length > 1;
89390                 })
89391                 .title(function() {
89392                     return _t('inspector.title_count', { title: _t('inspector.features'), count: _selectedIDs.length });
89393                 })
89394                 .disclosureContent(renderDisclosureContent);
89395
89396             context.history()
89397                 .on('change.selectionList', function(difference) {
89398                     if (difference) {
89399                         section.reRender();
89400                     }
89401                 });
89402
89403             section.entityIDs = function(val) {
89404                 if (!arguments.length) return _selectedIDs;
89405                 _selectedIDs = val;
89406                 return section;
89407             };
89408
89409             function selectEntity(entity) {
89410                 context.enter(modeSelect(context, [entity.id]));
89411             }
89412
89413             function deselectEntity(entity) {
89414                 event.stopPropagation();
89415
89416                 var selectedIDs = _selectedIDs.slice();
89417                 var index = selectedIDs.indexOf(entity.id);
89418                 if (index > -1) {
89419                     selectedIDs.splice(index, 1);
89420                     context.enter(modeSelect(context, selectedIDs));
89421                 }
89422             }
89423
89424             function renderDisclosureContent(selection) {
89425
89426                 var list = selection.selectAll('.feature-list')
89427                     .data([0]);
89428
89429                 list = list.enter()
89430                     .append('div')
89431                     .attr('class', 'feature-list')
89432                     .merge(list);
89433
89434                 var entities = _selectedIDs
89435                     .map(function(id) { return context.hasEntity(id); })
89436                     .filter(Boolean);
89437
89438                 var items = list.selectAll('.feature-list-item')
89439                     .data(entities, osmEntity.key);
89440
89441                 items.exit()
89442                     .remove();
89443
89444                 // Enter
89445                 var enter = items.enter()
89446                     .append('div')
89447                     .attr('class', 'feature-list-item')
89448                     .on('click', selectEntity);
89449
89450                 enter
89451                     .each(function(d) {
89452                         select(this).on('mouseover', function() {
89453                             utilHighlightEntities([d.id], true, context);
89454                         });
89455                         select(this).on('mouseout', function() {
89456                             utilHighlightEntities([d.id], false, context);
89457                         });
89458                     });
89459
89460                 var label = enter
89461                     .append('button')
89462                     .attr('class', 'label');
89463
89464                 enter
89465                     .append('button')
89466                     .attr('class', 'close')
89467                     .attr('title', _t('icons.deselect'))
89468                     .on('click', deselectEntity)
89469                     .call(svgIcon('#iD-icon-close'));
89470
89471                 label
89472                     .append('span')
89473                     .attr('class', 'entity-geom-icon')
89474                     .call(svgIcon('', 'pre-text'));
89475
89476                 label
89477                     .append('span')
89478                     .attr('class', 'entity-type');
89479
89480                 label
89481                     .append('span')
89482                     .attr('class', 'entity-name');
89483
89484                 // Update
89485                 items = items.merge(enter);
89486
89487                 items.selectAll('.entity-geom-icon use')
89488                     .attr('href', function() {
89489                         var entity = this.parentNode.parentNode.__data__;
89490                         return '#iD-icon-' + entity.geometry(context.graph());
89491                     });
89492
89493                 items.selectAll('.entity-type')
89494                     .text(function(entity) { return _mainPresetIndex.match(entity, context.graph()).name(); });
89495
89496                 items.selectAll('.entity-name')
89497                     .text(function(d) {
89498                         // fetch latest entity
89499                         var entity = context.entity(d.id);
89500                         return utilDisplayName(entity);
89501                     });
89502             }
89503
89504             return section;
89505         }
89506
89507         function uiEntityEditor(context) {
89508             var dispatch$1 = dispatch('choose');
89509             var _state = 'select';
89510             var _coalesceChanges = false;
89511             var _modified = false;
89512             var _base;
89513             var _entityIDs;
89514             var _activePresets = [];
89515             var _newFeature;
89516
89517             var _sections;
89518
89519             function entityEditor(selection) {
89520
89521                 var combinedTags = utilCombinedTags(_entityIDs, context.graph());
89522
89523                 // Header
89524                 var header = selection.selectAll('.header')
89525                     .data([0]);
89526
89527                 // Enter
89528                 var headerEnter = header.enter()
89529                     .append('div')
89530                     .attr('class', 'header fillL cf');
89531
89532                 headerEnter
89533                     .append('button')
89534                     .attr('class', 'preset-reset preset-choose')
89535                     .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-forward' : '#iD-icon-backward'));
89536
89537                 headerEnter
89538                     .append('button')
89539                     .attr('class', 'close')
89540                     .on('click', function() { context.enter(modeBrowse(context)); })
89541                     .call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));
89542
89543                 headerEnter
89544                     .append('h3');
89545
89546                 // Update
89547                 header = header
89548                     .merge(headerEnter);
89549
89550                 header.selectAll('h3')
89551                     .text(_entityIDs.length === 1 ? _t('inspector.edit') : _t('inspector.edit_features'));
89552
89553                 header.selectAll('.preset-reset')
89554                     .on('click', function() {
89555                         dispatch$1.call('choose', this, _activePresets);
89556                     });
89557
89558                 // Body
89559                 var body = selection.selectAll('.inspector-body')
89560                     .data([0]);
89561
89562                 // Enter
89563                 var bodyEnter = body.enter()
89564                     .append('div')
89565                     .attr('class', 'entity-editor inspector-body sep-top');
89566
89567                 // Update
89568                 body = body
89569                     .merge(bodyEnter);
89570
89571                 if (!_sections) {
89572                     _sections = [
89573                         uiSectionSelectionList(context),
89574                         uiSectionFeatureType(context).on('choose', function(presets) {
89575                             dispatch$1.call('choose', this, presets);
89576                         }),
89577                         uiSectionEntityIssues(context),
89578                         uiSectionPresetFields(context).on('change', changeTags).on('revert', revertTags),
89579                         uiSectionRawTagEditor('raw-tag-editor', context).on('change', changeTags),
89580                         uiSectionRawMemberEditor(context),
89581                         uiSectionRawMembershipEditor(context)
89582                     ];
89583                 }
89584
89585                 _sections.forEach(function(section) {
89586                     if (section.entityIDs) {
89587                         section.entityIDs(_entityIDs);
89588                     }
89589                     if (section.presets) {
89590                         section.presets(_activePresets);
89591                     }
89592                     if (section.tags) {
89593                         section.tags(combinedTags);
89594                     }
89595                     if (section.state) {
89596                         section.state(_state);
89597                     }
89598                     body.call(section.render);
89599                 });
89600
89601                 body
89602                     .selectAll('.key-trap-wrap')
89603                     .data([0])
89604                     .enter()
89605                     .append('div')
89606                     .attr('class', 'key-trap-wrap')
89607                     .append('input')
89608                     .attr('type', 'text')
89609                     .attr('class', 'key-trap')
89610                     .on('keydown.key-trap', function() {
89611                         // On tabbing, send focus back to the first field on the inspector-body
89612                         // (probably the `name` field) #4159
89613                         if (event.keyCode === 9 && !event.shiftKey) {
89614                             event.preventDefault();
89615                             body.select('input').node().focus();
89616                         }
89617                     });
89618
89619                 context.history()
89620                     .on('change.entity-editor', historyChanged);
89621
89622                 function historyChanged(difference) {
89623                     if (selection.selectAll('.entity-editor').empty()) return;
89624                     if (_state === 'hide') return;
89625                     var significant = !difference ||
89626                             difference.didChange.properties ||
89627                             difference.didChange.addition ||
89628                             difference.didChange.deletion;
89629                     if (!significant) return;
89630
89631                     _entityIDs = _entityIDs.filter(context.hasEntity);
89632                     if (!_entityIDs.length) return;
89633
89634                     var priorActivePreset = _activePresets.length === 1 && _activePresets[0];
89635
89636                     loadActivePresets();
89637
89638                     var graph = context.graph();
89639                     entityEditor.modified(_base !== graph);
89640                     entityEditor(selection);
89641
89642                     if (priorActivePreset && _activePresets.length === 1 && priorActivePreset !== _activePresets[0]) {
89643                         // flash the button to indicate the preset changed
89644                         context.container().selectAll('.entity-editor button.preset-reset .label')
89645                             .style('background-color', '#fff')
89646                             .transition()
89647                             .duration(750)
89648                             .style('background-color', null);
89649                     }
89650                 }
89651             }
89652
89653
89654             // Tag changes that fire on input can all get coalesced into a single
89655             // history operation when the user leaves the field.  #2342
89656             // Use explicit entityIDs in case the selection changes before the event is fired.
89657             function changeTags(entityIDs, changed, onInput) {
89658
89659                 var actions = [];
89660                 for (var i in entityIDs) {
89661                     var entityID = entityIDs[i];
89662                     var entity = context.entity(entityID);
89663
89664                     var tags = Object.assign({}, entity.tags);   // shallow copy
89665
89666                     for (var k in changed) {
89667                         if (!k) continue;
89668                         var v = changed[k];
89669                         if (v !== undefined || tags.hasOwnProperty(k)) {
89670                             tags[k] = v;
89671                         }
89672                     }
89673
89674                     if (!onInput) {
89675                         tags = utilCleanTags(tags);
89676                     }
89677
89678                     if (!fastDeepEqual(entity.tags, tags)) {
89679                         actions.push(actionChangeTags(entityID, tags));
89680                     }
89681                 }
89682
89683                 if (actions.length) {
89684                     var combinedAction = function(graph) {
89685                         actions.forEach(function(action) {
89686                             graph = action(graph);
89687                         });
89688                         return graph;
89689                     };
89690
89691                     var annotation = _t('operations.change_tags.annotation');
89692
89693                     if (_coalesceChanges) {
89694                         context.overwrite(combinedAction, annotation);
89695                     } else {
89696                         context.perform(combinedAction, annotation);
89697                         _coalesceChanges = !!onInput;
89698                     }
89699                 }
89700
89701                 // if leaving field (blur event), rerun validation
89702                 if (!onInput) {
89703                     context.validator().validate();
89704                 }
89705             }
89706
89707             function revertTags(keys) {
89708
89709                 var actions = [];
89710                 for (var i in _entityIDs) {
89711                     var entityID = _entityIDs[i];
89712
89713                     var original = context.graph().base().entities[entityID];
89714                     var changed = {};
89715                     for (var j in keys) {
89716                         var key = keys[j];
89717                         changed[key] = original ? original.tags[key] : undefined;
89718                     }
89719
89720                     var entity = context.entity(entityID);
89721                     var tags = Object.assign({}, entity.tags);   // shallow copy
89722
89723                     for (var k in changed) {
89724                         if (!k) continue;
89725                         var v = changed[k];
89726                         if (v !== undefined || tags.hasOwnProperty(k)) {
89727                             tags[k] = v;
89728                         }
89729                     }
89730
89731
89732                     tags = utilCleanTags(tags);
89733
89734                     if (!fastDeepEqual(entity.tags, tags)) {
89735                         actions.push(actionChangeTags(entityID, tags));
89736                     }
89737
89738                 }
89739
89740                 if (actions.length) {
89741                     var combinedAction = function(graph) {
89742                         actions.forEach(function(action) {
89743                             graph = action(graph);
89744                         });
89745                         return graph;
89746                     };
89747
89748                     var annotation = _t('operations.change_tags.annotation');
89749
89750                     if (_coalesceChanges) {
89751                         context.overwrite(combinedAction, annotation);
89752                     } else {
89753                         context.perform(combinedAction, annotation);
89754                         _coalesceChanges = false;
89755                     }
89756                 }
89757
89758                 context.validator().validate();
89759             }
89760
89761
89762             entityEditor.modified = function(val) {
89763                 if (!arguments.length) return _modified;
89764                 _modified = val;
89765                 return entityEditor;
89766             };
89767
89768
89769             entityEditor.state = function(val) {
89770                 if (!arguments.length) return _state;
89771                 _state = val;
89772                 return entityEditor;
89773             };
89774
89775
89776             entityEditor.entityIDs = function(val) {
89777                 if (!arguments.length) return _entityIDs;
89778                 if (val && _entityIDs && utilArrayIdentical(_entityIDs, val)) return entityEditor;  // exit early if no change
89779
89780                 _entityIDs = val;
89781                 _base = context.graph();
89782                 _coalesceChanges = false;
89783
89784                 loadActivePresets();
89785
89786                 return entityEditor
89787                     .modified(false);
89788             };
89789
89790
89791             entityEditor.newFeature = function(val) {
89792                 if (!arguments.length) return _newFeature;
89793                 _newFeature = val;
89794                 return entityEditor;
89795             };
89796
89797
89798             function loadActivePresets() {
89799
89800                 var graph = context.graph();
89801
89802                 var counts = {};
89803
89804                 for (var i in _entityIDs) {
89805                     var entity = graph.hasEntity(_entityIDs[i]);
89806                     if (!entity) return;
89807
89808                     var match = _mainPresetIndex.match(entity, graph);
89809
89810                     if (!counts[match.id]) counts[match.id] = 0;
89811                     counts[match.id] += 1;
89812                 }
89813
89814                 var matches = Object.keys(counts).sort(function(p1, p2) {
89815                     return counts[p2] - counts[p1];
89816                 }).map(function(pID) {
89817                     return _mainPresetIndex.item(pID);
89818                 });
89819
89820                 // A "weak" preset doesn't set any tags. (e.g. "Address")
89821                 var weakPreset = _activePresets.length === 1 &&
89822                     Object.keys(_activePresets[0].addTags || {}).length === 0;
89823                 // Don't replace a weak preset with a fallback preset (e.g. "Point")
89824                 if (weakPreset && matches.length === 1 && matches[0].isFallback()) return;
89825
89826                 entityEditor.presets(matches);
89827             }
89828
89829             entityEditor.presets = function(val) {
89830                 if (!arguments.length) return _activePresets;
89831
89832                 // don't reload the same preset
89833                 if (!utilArrayIdentical(val, _activePresets)) {
89834                     _activePresets = val;
89835                 }
89836                 return entityEditor;
89837             };
89838
89839             return utilRebind(entityEditor, dispatch$1, 'on');
89840         }
89841
89842         function uiPresetList(context) {
89843             var dispatch$1 = dispatch('cancel', 'choose');
89844             var _entityIDs;
89845             var _currentPresets;
89846             var _autofocus = false;
89847
89848
89849             function presetList(selection) {
89850                 if (!_entityIDs) return;
89851
89852                 var presets = _mainPresetIndex.matchAllGeometry(entityGeometries());
89853
89854                 selection.html('');
89855
89856                 var messagewrap = selection
89857                     .append('div')
89858                     .attr('class', 'header fillL');
89859
89860                 var message = messagewrap
89861                     .append('h3')
89862                     .text(_t('inspector.choose'));
89863
89864                 messagewrap
89865                     .append('button')
89866                     .attr('class', 'preset-choose')
89867                     .on('click', function() { dispatch$1.call('cancel', this); })
89868                     .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward'));
89869
89870                 function initialKeydown() {
89871                     // hack to let delete shortcut work when search is autofocused
89872                     if (search.property('value').length === 0 &&
89873                         (event.keyCode === utilKeybinding.keyCodes['⌫'] ||
89874                          event.keyCode === utilKeybinding.keyCodes['⌦'])) {
89875                         event.preventDefault();
89876                         event.stopPropagation();
89877                         operationDelete(context, _entityIDs)();
89878
89879                     // hack to let undo work when search is autofocused
89880                     } else if (search.property('value').length === 0 &&
89881                         (event.ctrlKey || event.metaKey) &&
89882                         event.keyCode === utilKeybinding.keyCodes.z) {
89883                         event.preventDefault();
89884                         event.stopPropagation();
89885                         context.undo();
89886                     } else if (!event.ctrlKey && !event.metaKey) {
89887                         // don't check for delete/undo hack on future keydown events
89888                         select(this).on('keydown', keydown);
89889                         keydown.call(this);
89890                     }
89891                 }
89892
89893                 function keydown() {
89894                     // down arrow
89895                     if (event.keyCode === utilKeybinding.keyCodes['↓'] &&
89896                         // if insertion point is at the end of the string
89897                         search.node().selectionStart === search.property('value').length) {
89898                         event.preventDefault();
89899                         event.stopPropagation();
89900                         // move focus to the first item in the preset list
89901                         var buttons = list.selectAll('.preset-list-button');
89902                         if (!buttons.empty()) buttons.nodes()[0].focus();
89903                     }
89904                 }
89905
89906                 function keypress() {
89907                     // enter
89908                     var value = search.property('value');
89909                     if (event.keyCode === 13 && value.length) {
89910                         list.selectAll('.preset-list-item:first-child')
89911                             .each(function(d) { d.choose.call(this); });
89912                     }
89913                 }
89914
89915                 function inputevent() {
89916                     var value = search.property('value');
89917                     list.classed('filtered', value.length);
89918                     var extent = combinedEntityExtent();
89919                     var results, messageText;
89920                     if (value.length && extent) {
89921                         var center = extent.center();
89922                         var countryCode = iso1A2Code(center);
89923
89924                         results = presets.search(value, entityGeometries()[0], countryCode && countryCode.toLowerCase());
89925                         messageText = _t('inspector.results', {
89926                             n: results.collection.length,
89927                             search: value
89928                         });
89929                     } else {
89930                         results = _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro());
89931                         messageText = _t('inspector.choose');
89932                     }
89933                     list.call(drawList, results);
89934                     message.text(messageText);
89935                 }
89936
89937                 var searchWrap = selection
89938                     .append('div')
89939                     .attr('class', 'search-header');
89940
89941                 var search = searchWrap
89942                     .append('input')
89943                     .attr('class', 'preset-search-input')
89944                     .attr('placeholder', _t('inspector.search'))
89945                     .attr('type', 'search')
89946                     .call(utilNoAuto)
89947                     .on('keydown', initialKeydown)
89948                     .on('keypress', keypress)
89949                     .on('input', inputevent);
89950
89951                 searchWrap
89952                     .call(svgIcon('#iD-icon-search', 'pre-text'));
89953
89954                 if (_autofocus) {
89955                     search.node().focus();
89956                 }
89957
89958                 var listWrap = selection
89959                     .append('div')
89960                     .attr('class', 'inspector-body');
89961
89962                 var list = listWrap
89963                     .append('div')
89964                     .attr('class', 'preset-list')
89965                     .call(drawList, _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro()));
89966
89967                 context.features().on('change.preset-list', updateForFeatureHiddenState);
89968             }
89969
89970
89971             function drawList(list, presets) {
89972                 presets = presets.matchAllGeometry(entityGeometries());
89973                 var collection = presets.collection.reduce(function(collection, preset) {
89974                     if (!preset) return collection;
89975
89976                     if (preset.members) {
89977                         if (preset.members.collection.filter(function(preset) {
89978                             return preset.addable();
89979                         }).length > 1) {
89980                             collection.push(CategoryItem(preset));
89981                         }
89982                     } else if (preset.addable()) {
89983                         collection.push(PresetItem(preset));
89984                     }
89985                     return collection;
89986                 }, []);
89987
89988                 var items = list.selectAll('.preset-list-item')
89989                     .data(collection, function(d) { return d.preset.id; });
89990
89991                 items.order();
89992
89993                 items.exit()
89994                     .remove();
89995
89996                 items.enter()
89997                     .append('div')
89998                     .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); })
89999                     .classed('current', function(item) { return _currentPresets.indexOf(item.preset) !== -1; })
90000                     .each(function(item) { select(this).call(item); })
90001                     .style('opacity', 0)
90002                     .transition()
90003                     .style('opacity', 1);
90004
90005                 updateForFeatureHiddenState();
90006             }
90007
90008             function itemKeydown(){
90009                 // the actively focused item
90010                 var item = select(this.closest('.preset-list-item'));
90011                 var parentItem = select(item.node().parentNode.closest('.preset-list-item'));
90012
90013                 // arrow down, move focus to the next, lower item
90014                 if (event.keyCode === utilKeybinding.keyCodes['↓']) {
90015                     event.preventDefault();
90016                     event.stopPropagation();
90017                     // the next item in the list at the same level
90018                     var nextItem = select(item.node().nextElementSibling);
90019                     // if there is no next item in this list
90020                     if (nextItem.empty()) {
90021                         // if there is a parent item
90022                         if (!parentItem.empty()) {
90023                             // the item is the last item of a sublist,
90024                             // select the next item at the parent level
90025                             nextItem = select(parentItem.node().nextElementSibling);
90026                         }
90027                     // if the focused item is expanded
90028                     } else if (select(this).classed('expanded')) {
90029                         // select the first subitem instead
90030                         nextItem = item.select('.subgrid .preset-list-item:first-child');
90031                     }
90032                     if (!nextItem.empty()) {
90033                         // focus on the next item
90034                         nextItem.select('.preset-list-button').node().focus();
90035                     }
90036
90037                 // arrow up, move focus to the previous, higher item
90038                 } else if (event.keyCode === utilKeybinding.keyCodes['↑']) {
90039                     event.preventDefault();
90040                     event.stopPropagation();
90041                     // the previous item in the list at the same level
90042                     var previousItem = select(item.node().previousElementSibling);
90043
90044                     // if there is no previous item in this list
90045                     if (previousItem.empty()) {
90046                         // if there is a parent item
90047                         if (!parentItem.empty()) {
90048                             // the item is the first subitem of a sublist select the parent item
90049                             previousItem = parentItem;
90050                         }
90051                     // if the previous item is expanded
90052                     } else if (previousItem.select('.preset-list-button').classed('expanded')) {
90053                         // select the last subitem of the sublist of the previous item
90054                         previousItem = previousItem.select('.subgrid .preset-list-item:last-child');
90055                     }
90056
90057                     if (!previousItem.empty()) {
90058                         // focus on the previous item
90059                         previousItem.select('.preset-list-button').node().focus();
90060                     } else {
90061                         // the focus is at the top of the list, move focus back to the search field
90062                         var search = select(this.closest('.preset-list-pane')).select('.preset-search-input');
90063                         search.node().focus();
90064                     }
90065
90066                 // arrow left, move focus to the parent item if there is one
90067                 } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '→' : '←']) {
90068                     event.preventDefault();
90069                     event.stopPropagation();
90070                     // if there is a parent item, focus on the parent item
90071                     if (!parentItem.empty()) {
90072                         parentItem.select('.preset-list-button').node().focus();
90073                     }
90074
90075                 // arrow right, choose this item
90076                 } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '←' : '→']) {
90077                     event.preventDefault();
90078                     event.stopPropagation();
90079                     item.datum().choose.call(select(this).node());
90080                 }
90081             }
90082
90083
90084             function CategoryItem(preset) {
90085                 var box, sublist, shown = false;
90086
90087                 function item(selection) {
90088                     var wrap = selection.append('div')
90089                         .attr('class', 'preset-list-button-wrap category');
90090
90091                     function click() {
90092                         var isExpanded = select(this).classed('expanded');
90093                         var iconName = isExpanded ?
90094                             (_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward') : '#iD-icon-down';
90095                         select(this)
90096                             .classed('expanded', !isExpanded);
90097                         select(this).selectAll('div.label-inner svg.icon use')
90098                             .attr('href', iconName);
90099                         item.choose();
90100                     }
90101
90102                     var geometries = entityGeometries();
90103
90104                     var button = wrap
90105                         .append('button')
90106                         .attr('class', 'preset-list-button')
90107                         .classed('expanded', false)
90108                         .call(uiPresetIcon()
90109                             .geometry(geometries.length === 1 && geometries[0])
90110                             .preset(preset))
90111                         .on('click', click)
90112                         .on('keydown', function() {
90113                             // right arrow, expand the focused item
90114                             if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '←' : '→']) {
90115                                 event.preventDefault();
90116                                 event.stopPropagation();
90117                                 // if the item isn't expanded
90118                                 if (!select(this).classed('expanded')) {
90119                                     // toggle expansion (expand the item)
90120                                     click.call(this);
90121                                 }
90122                             // left arrow, collapse the focused item
90123                             } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '→' : '←']) {
90124                                 event.preventDefault();
90125                                 event.stopPropagation();
90126                                 // if the item is expanded
90127                                 if (select(this).classed('expanded')) {
90128                                     // toggle expansion (collapse the item)
90129                                     click.call(this);
90130                                 }
90131                             } else {
90132                                 itemKeydown.call(this);
90133                             }
90134                         });
90135
90136                     var label = button
90137                         .append('div')
90138                         .attr('class', 'label')
90139                         .append('div')
90140                         .attr('class', 'label-inner');
90141
90142                     label
90143                         .append('div')
90144                         .attr('class', 'namepart')
90145                         .call(svgIcon((_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'))
90146                         .append('span')
90147                         .html(function() { return preset.name() + '&hellip;'; });
90148
90149                     box = selection.append('div')
90150                         .attr('class', 'subgrid')
90151                         .style('max-height', '0px')
90152                         .style('opacity', 0);
90153
90154                     box.append('div')
90155                         .attr('class', 'arrow');
90156
90157                     sublist = box.append('div')
90158                         .attr('class', 'preset-list fillL3');
90159                 }
90160
90161
90162                 item.choose = function() {
90163                     if (!box || !sublist) return;
90164
90165                     if (shown) {
90166                         shown = false;
90167                         box.transition()
90168                             .duration(200)
90169                             .style('opacity', '0')
90170                             .style('max-height', '0px')
90171                             .style('padding-bottom', '0px');
90172                     } else {
90173                         shown = true;
90174                         var members = preset.members.matchAllGeometry(entityGeometries());
90175                         sublist.call(drawList, members);
90176                         box.transition()
90177                             .duration(200)
90178                             .style('opacity', '1')
90179                             .style('max-height', 200 + members.collection.length * 190 + 'px')
90180                             .style('padding-bottom', '10px');
90181                     }
90182                 };
90183
90184                 item.preset = preset;
90185                 return item;
90186             }
90187
90188
90189             function PresetItem(preset) {
90190                 function item(selection) {
90191                     var wrap = selection.append('div')
90192                         .attr('class', 'preset-list-button-wrap');
90193
90194                     var geometries = entityGeometries();
90195
90196                     var button = wrap.append('button')
90197                         .attr('class', 'preset-list-button')
90198                         .call(uiPresetIcon()
90199                             .geometry(geometries.length === 1 && geometries[0])
90200                             .preset(preset))
90201                         .on('click', item.choose)
90202                         .on('keydown', itemKeydown);
90203
90204                     var label = button
90205                         .append('div')
90206                         .attr('class', 'label')
90207                         .append('div')
90208                         .attr('class', 'label-inner');
90209
90210                     // NOTE: split/join on en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc)
90211                     label.selectAll('.namepart')
90212                         .data(preset.name().split(' – '))
90213                         .enter()
90214                         .append('div')
90215                         .attr('class', 'namepart')
90216                         .text(function(d) { return d; });
90217
90218                     wrap.call(item.reference.button);
90219                     selection.call(item.reference.body);
90220                 }
90221
90222                 item.choose = function() {
90223                     if (select(this).classed('disabled')) return;
90224                     if (!context.inIntro()) {
90225                         _mainPresetIndex.setMostRecent(preset, entityGeometries()[0]);
90226                     }
90227                     context.perform(
90228                         function(graph) {
90229                             for (var i in _entityIDs) {
90230                                 var entityID = _entityIDs[i];
90231                                 var oldPreset = _mainPresetIndex.match(graph.entity(entityID), graph);
90232                                 graph = actionChangePreset(entityID, oldPreset, preset)(graph);
90233                             }
90234                             return graph;
90235                         },
90236                         _t('operations.change_tags.annotation')
90237                     );
90238
90239                     context.validator().validate();  // rerun validation
90240                     dispatch$1.call('choose', this, preset);
90241                 };
90242
90243                 item.help = function() {
90244                     event.stopPropagation();
90245                     item.reference.toggle();
90246                 };
90247
90248                 item.preset = preset;
90249                 item.reference = uiTagReference(preset.reference(entityGeometries()[0]));
90250
90251                 return item;
90252             }
90253
90254
90255             function updateForFeatureHiddenState() {
90256                 if (!_entityIDs.every(context.hasEntity)) return;
90257
90258                 var geometries = entityGeometries();
90259                 var button = context.container().selectAll('.preset-list .preset-list-button');
90260
90261                 // remove existing tooltips
90262                 button.call(uiTooltip().destroyAny);
90263
90264                 button.each(function(item, index) {
90265                     var hiddenPresetFeaturesId;
90266                     for (var i in geometries) {
90267                         hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, geometries[i]);
90268                         if (hiddenPresetFeaturesId) break;
90269                     }
90270                     var isHiddenPreset = !context.inIntro() &&
90271                         !!hiddenPresetFeaturesId &&
90272                         (_currentPresets.length !== 1 || item.preset !== _currentPresets[0]);
90273
90274                     select(this)
90275                         .classed('disabled', isHiddenPreset);
90276
90277                     if (isHiddenPreset) {
90278                         var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId);
90279                         var tooltipIdSuffix = isAutoHidden ? 'zoom' : 'manual';
90280                         var tooltipObj = { features: _t('feature.' + hiddenPresetFeaturesId + '.description') };
90281                         select(this).call(uiTooltip()
90282                             .title(_t('inspector.hidden_preset.' + tooltipIdSuffix, tooltipObj))
90283                             .placement(index < 2 ? 'bottom' : 'top')
90284                         );
90285                     }
90286                 });
90287             }
90288
90289             presetList.autofocus = function(val) {
90290                 if (!arguments.length) return _autofocus;
90291                 _autofocus = val;
90292                 return presetList;
90293             };
90294
90295             presetList.entityIDs = function(val) {
90296                 if (!arguments.length) return _entityIDs;
90297                 _entityIDs = val;
90298                 if (_entityIDs && _entityIDs.length) {
90299                     var presets = _entityIDs.map(function(entityID) {
90300                         return _mainPresetIndex.match(context.entity(entityID), context.graph());
90301                     });
90302                     presetList.presets(presets);
90303                 }
90304                 return presetList;
90305             };
90306
90307             presetList.presets = function(val) {
90308                 if (!arguments.length) return _currentPresets;
90309                 _currentPresets = val;
90310                 return presetList;
90311             };
90312
90313             function entityGeometries() {
90314
90315                 var counts = {};
90316
90317                 for (var i in _entityIDs) {
90318                     var entityID = _entityIDs[i];
90319                     var entity = context.entity(entityID);
90320                     var geometry = entity.geometry(context.graph());
90321
90322                     // Treat entities on addr:interpolation lines as points, not vertices (#3241)
90323                     if (geometry === 'vertex' && entity.isOnAddressLine(context.graph())) {
90324                         geometry = 'point';
90325                     }
90326
90327                     if (!counts[geometry]) counts[geometry] = 0;
90328                     counts[geometry] += 1;
90329                 }
90330
90331                 return Object.keys(counts).sort(function(geom1, geom2) {
90332                     return counts[geom2] - counts[geom1];
90333                 });
90334             }
90335
90336             function combinedEntityExtent() {
90337                 return _entityIDs.reduce(function(extent, entityID) {
90338                     var entity = context.graph().entity(entityID);
90339                     return extent.extend(entity.extent(context.graph()));
90340                 }, geoExtent());
90341             }
90342
90343             return utilRebind(presetList, dispatch$1, 'on');
90344         }
90345
90346         function uiInspector(context) {
90347             var presetList = uiPresetList(context);
90348             var entityEditor = uiEntityEditor(context);
90349             var wrap = select(null),
90350                 presetPane = select(null),
90351                 editorPane = select(null);
90352             var _state = 'select';
90353             var _entityIDs;
90354             var _newFeature = false;
90355
90356
90357             function inspector(selection) {
90358                 presetList
90359                     .entityIDs(_entityIDs)
90360                     .autofocus(_newFeature)
90361                     .on('choose', inspector.setPreset)
90362                     .on('cancel', function() {
90363                         wrap.transition()
90364                             .styleTween('right', function() { return interpolate('-100%', '0%'); });
90365                         editorPane.call(entityEditor);
90366                     });
90367
90368                 entityEditor
90369                     .state(_state)
90370                     .entityIDs(_entityIDs)
90371                     .on('choose', inspector.showList);
90372
90373                 wrap = selection.selectAll('.panewrap')
90374                     .data([0]);
90375
90376                 var enter = wrap.enter()
90377                     .append('div')
90378                     .attr('class', 'panewrap');
90379
90380                 enter
90381                     .append('div')
90382                     .attr('class', 'preset-list-pane pane');
90383
90384                 enter
90385                     .append('div')
90386                     .attr('class', 'entity-editor-pane pane');
90387
90388                 wrap = wrap.merge(enter);
90389                 presetPane = wrap.selectAll('.preset-list-pane');
90390                 editorPane = wrap.selectAll('.entity-editor-pane');
90391
90392                 function shouldDefaultToPresetList() {
90393                     // always show the inspector on hover
90394                     if (_state !== 'select') return false;
90395
90396                     // can only change preset on single selection
90397                     if (_entityIDs.length !== 1) return false;
90398
90399                     var entityID = _entityIDs[0];
90400                     var entity = context.hasEntity(entityID);
90401                     if (!entity) return false;
90402
90403                     // default to inspector if there are already tags
90404                     if (entity.hasNonGeometryTags()) return false;
90405
90406                     // prompt to select preset if feature is new and untagged
90407                     if (_newFeature) return true;
90408
90409                     // all existing features except vertices should default to inspector
90410                     if (entity.geometry(context.graph()) !== 'vertex') return false;
90411
90412                     // show vertex relations if any
90413                     if (context.graph().parentRelations(entity).length) return false;
90414
90415                     // show vertex issues if there are any
90416                     if (context.validator().getEntityIssues(entityID).length) return false;
90417
90418                     // show turn retriction editor for junction vertices
90419                     if (entity.isHighwayIntersection(context.graph())) return false;
90420
90421                     // otherwise show preset list for uninteresting vertices
90422                     return true;
90423                 }
90424
90425                 if (shouldDefaultToPresetList()) {
90426                     wrap.style('right', '-100%');
90427                     presetPane.call(presetList);
90428                 } else {
90429                     wrap.style('right', '0%');
90430                     editorPane.call(entityEditor);
90431                 }
90432
90433                 var footer = selection.selectAll('.footer')
90434                     .data([0]);
90435
90436                 footer = footer.enter()
90437                     .append('div')
90438                     .attr('class', 'footer')
90439                     .merge(footer);
90440
90441                 footer
90442                     .call(uiViewOnOSM(context)
90443                         .what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0]))
90444                     );
90445             }
90446
90447             inspector.showList = function(presets) {
90448
90449                 wrap.transition()
90450                     .styleTween('right', function() { return interpolate('0%', '-100%'); });
90451
90452                 if (presets) {
90453                     presetList.presets(presets);
90454                 }
90455
90456                 presetPane
90457                     .call(presetList.autofocus(true));
90458             };
90459
90460             inspector.setPreset = function(preset) {
90461
90462                 // upon setting multipolygon, go to the area preset list instead of the editor
90463                 if (preset.id === 'type/multipolygon') {
90464                     presetPane
90465                         .call(presetList.autofocus(true));
90466
90467                 } else {
90468                     wrap.transition()
90469                         .styleTween('right', function() { return interpolate('-100%', '0%'); });
90470
90471                     editorPane
90472                         .call(entityEditor.presets([preset]));
90473                 }
90474
90475             };
90476
90477             inspector.state = function(val) {
90478                 if (!arguments.length) return _state;
90479                 _state = val;
90480                 entityEditor.state(_state);
90481
90482                 // remove any old field help overlay that might have gotten attached to the inspector
90483                 context.container().selectAll('.field-help-body').remove();
90484
90485                 return inspector;
90486             };
90487
90488
90489             inspector.entityIDs = function(val) {
90490                 if (!arguments.length) return _entityIDs;
90491                 _entityIDs = val;
90492                 return inspector;
90493             };
90494
90495
90496             inspector.newFeature = function(val) {
90497                 if (!arguments.length) return _newFeature;
90498                 _newFeature = val;
90499                 return inspector;
90500             };
90501
90502
90503             return inspector;
90504         }
90505
90506         function uiSidebar(context) {
90507             var inspector = uiInspector(context);
90508             var dataEditor = uiDataEditor(context);
90509             var noteEditor = uiNoteEditor(context);
90510             var improveOsmEditor = uiImproveOsmEditor(context);
90511             var keepRightEditor = uiKeepRightEditor(context);
90512             var osmoseEditor = uiOsmoseEditor(context);
90513             var _current;
90514             var _wasData = false;
90515             var _wasNote = false;
90516             var _wasQaItem = false;
90517
90518             // use pointer events on supported platforms; fallback to mouse events
90519             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
90520
90521
90522             function sidebar(selection) {
90523                 var container = context.container();
90524                 var minWidth = 240;
90525                 var sidebarWidth;
90526                 var containerWidth;
90527                 var dragOffset;
90528
90529                 // Set the initial width constraints
90530                 selection
90531                     .style('min-width', minWidth + 'px')
90532                     .style('max-width', '400px')
90533                     .style('width', '33.3333%');
90534
90535                 var resizer = selection
90536                     .append('div')
90537                     .attr('class', 'sidebar-resizer')
90538                     .on(_pointerPrefix + 'down.sidebar-resizer', pointerdown);
90539
90540                 var downPointerId, lastClientX, containerLocGetter;
90541
90542                 function pointerdown() {
90543                     if (downPointerId) return;
90544
90545                     if ('button' in event && event.button !== 0) return;
90546
90547                     downPointerId = event.pointerId || 'mouse';
90548
90549                     lastClientX = event.clientX;
90550
90551                     containerLocGetter = utilFastMouse(container.node());
90552
90553                     // offset from edge of sidebar-resizer
90554                     dragOffset = utilFastMouse(resizer.node())(event)[0] - 1;
90555
90556                     sidebarWidth = selection.node().getBoundingClientRect().width;
90557                     containerWidth = container.node().getBoundingClientRect().width;
90558                     var widthPct = (sidebarWidth / containerWidth) * 100;
90559                     selection
90560                         .style('width', widthPct + '%')    // lock in current width
90561                         .style('max-width', '85%');        // but allow larger widths
90562
90563                     resizer.classed('dragging', true);
90564
90565                     select(window)
90566                         .on('touchmove.sidebar-resizer', function() {
90567                             // disable page scrolling while resizing on touch input
90568                             event.preventDefault();
90569                         }, { passive: false })
90570                         .on(_pointerPrefix + 'move.sidebar-resizer', pointermove)
90571                         .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', pointerup);
90572                 }
90573
90574                 function pointermove() {
90575
90576                     if (downPointerId !== (event.pointerId || 'mouse')) return;
90577
90578                     event.preventDefault();
90579
90580                     var dx = event.clientX - lastClientX;
90581
90582                     lastClientX = event.clientX;
90583
90584                     var isRTL = (_mainLocalizer.textDirection() === 'rtl');
90585                     var scaleX = isRTL ? 0 : 1;
90586                     var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
90587
90588                     var x = containerLocGetter(event)[0] - dragOffset;
90589                     sidebarWidth = isRTL ? containerWidth - x : x;
90590
90591                     var isCollapsed = selection.classed('collapsed');
90592                     var shouldCollapse = sidebarWidth < minWidth;
90593
90594                     selection.classed('collapsed', shouldCollapse);
90595
90596                     if (shouldCollapse) {
90597                         if (!isCollapsed) {
90598                             selection
90599                                 .style(xMarginProperty, '-400px')
90600                                 .style('width', '400px');
90601
90602                             context.ui().onResize([(sidebarWidth - dx) * scaleX, 0]);
90603                         }
90604
90605                     } else {
90606                         var widthPct = (sidebarWidth / containerWidth) * 100;
90607                         selection
90608                             .style(xMarginProperty, null)
90609                             .style('width', widthPct + '%');
90610
90611                         if (isCollapsed) {
90612                             context.ui().onResize([-sidebarWidth * scaleX, 0]);
90613                         } else {
90614                             context.ui().onResize([-dx * scaleX, 0]);
90615                         }
90616                     }
90617                 }
90618
90619                 function pointerup() {
90620                     if (downPointerId !== (event.pointerId || 'mouse')) return;
90621
90622                     downPointerId = null;
90623
90624                     resizer.classed('dragging', false);
90625
90626                     select(window)
90627                         .on('touchmove.sidebar-resizer', null)
90628                         .on(_pointerPrefix + 'move.sidebar-resizer', null)
90629                         .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', null);
90630                 }
90631
90632                 var featureListWrap = selection
90633                     .append('div')
90634                     .attr('class', 'feature-list-pane')
90635                     .call(uiFeatureList(context));
90636
90637                 var inspectorWrap = selection
90638                     .append('div')
90639                     .attr('class', 'inspector-hidden inspector-wrap');
90640
90641                 var hoverModeSelect = function(targets) {
90642                     context.container().selectAll('.feature-list-item').classed('hover', false);
90643
90644                     if (context.selectedIDs().length > 1 &&
90645                         targets && targets.length) {
90646
90647                         var elements = context.container().selectAll('.feature-list-item')
90648                             .filter(function (node) {
90649                                 return targets.indexOf(node) !== -1;
90650                             });
90651
90652                         if (!elements.empty()) {
90653                             elements.classed('hover', true);
90654                         }
90655                     }
90656                 };
90657
90658                 sidebar.hoverModeSelect = throttle(hoverModeSelect, 200);
90659
90660                 function hover(targets) {
90661                     var datum = targets && targets.length && targets[0];
90662                     if (datum && datum.__featurehash__) {   // hovering on data
90663                         _wasData = true;
90664                         sidebar
90665                             .show(dataEditor.datum(datum));
90666
90667                         selection.selectAll('.sidebar-component')
90668                             .classed('inspector-hover', true);
90669
90670                     } else if (datum instanceof osmNote) {
90671                         if (context.mode().id === 'drag-note') return;
90672                         _wasNote = true;
90673
90674                         var osm = services.osm;
90675                         if (osm) {
90676                             datum = osm.getNote(datum.id);   // marker may contain stale data - get latest
90677                         }
90678
90679                         sidebar
90680                             .show(noteEditor.note(datum));
90681
90682                         selection.selectAll('.sidebar-component')
90683                             .classed('inspector-hover', true);
90684
90685                     } else if (datum instanceof QAItem) {
90686                         _wasQaItem = true;
90687
90688                         var errService = services[datum.service];
90689                         if (errService) {
90690                             // marker may contain stale data - get latest
90691                             datum = errService.getError(datum.id);
90692                         }
90693
90694                         // Currently only three possible services
90695                         var errEditor;
90696                         if (datum.service === 'keepRight') {
90697                             errEditor = keepRightEditor;
90698                         } else if (datum.service === 'osmose') {
90699                             errEditor = osmoseEditor;
90700                         } else {
90701                             errEditor = improveOsmEditor;
90702                         }
90703
90704                         context.container().selectAll('.qaItem.' + datum.service)
90705                             .classed('hover', function(d) { return d.id === datum.id; });
90706
90707                         sidebar
90708                             .show(errEditor.error(datum));
90709
90710                         selection.selectAll('.sidebar-component')
90711                             .classed('inspector-hover', true);
90712
90713                     } else if (!_current && (datum instanceof osmEntity)) {
90714                         featureListWrap
90715                             .classed('inspector-hidden', true);
90716
90717                         inspectorWrap
90718                             .classed('inspector-hidden', false)
90719                             .classed('inspector-hover', true);
90720
90721                         if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') {
90722                             inspector
90723                                 .state('hover')
90724                                 .entityIDs([datum.id])
90725                                 .newFeature(false);
90726
90727                             inspectorWrap
90728                                 .call(inspector);
90729                         }
90730
90731                     } else if (!_current) {
90732                         featureListWrap
90733                             .classed('inspector-hidden', false);
90734                         inspectorWrap
90735                             .classed('inspector-hidden', true);
90736                         inspector
90737                             .state('hide');
90738
90739                     } else if (_wasData || _wasNote || _wasQaItem) {
90740                         _wasNote = false;
90741                         _wasData = false;
90742                         _wasQaItem = false;
90743                         context.container().selectAll('.note').classed('hover', false);
90744                         context.container().selectAll('.qaItem').classed('hover', false);
90745                         sidebar.hide();
90746                     }
90747                 }
90748
90749                 sidebar.hover = throttle(hover, 200);
90750
90751
90752                 sidebar.intersects = function(extent) {
90753                     var rect = selection.node().getBoundingClientRect();
90754                     return extent.intersects([
90755                         context.projection.invert([0, rect.height]),
90756                         context.projection.invert([rect.width, 0])
90757                     ]);
90758                 };
90759
90760
90761                 sidebar.select = function(ids, newFeature) {
90762                     sidebar.hide();
90763
90764                     if (ids && ids.length) {
90765
90766                         var entity = ids.length === 1 && context.entity(ids[0]);
90767                         if (entity && newFeature && selection.classed('collapsed')) {
90768                             // uncollapse the sidebar
90769                             var extent = entity.extent(context.graph());
90770                             sidebar.expand(sidebar.intersects(extent));
90771                         }
90772
90773                         featureListWrap
90774                             .classed('inspector-hidden', true);
90775
90776                         inspectorWrap
90777                             .classed('inspector-hidden', false)
90778                             .classed('inspector-hover', false);
90779
90780                         if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), ids) || inspector.state() !== 'select') {
90781                             inspector
90782                                 .state('select')
90783                                 .entityIDs(ids)
90784                                 .newFeature(newFeature);
90785
90786                             inspectorWrap
90787                                 .call(inspector);
90788                         }
90789
90790                     } else {
90791                         inspector
90792                             .state('hide');
90793                     }
90794                 };
90795
90796
90797                 sidebar.showPresetList = function() {
90798                     inspector.showList();
90799                 };
90800
90801
90802                 sidebar.show = function(component, element) {
90803                     featureListWrap
90804                         .classed('inspector-hidden', true);
90805                     inspectorWrap
90806                         .classed('inspector-hidden', true);
90807
90808                     if (_current) _current.remove();
90809                     _current = selection
90810                         .append('div')
90811                         .attr('class', 'sidebar-component')
90812                         .call(component, element);
90813                 };
90814
90815
90816                 sidebar.hide = function() {
90817                     featureListWrap
90818                         .classed('inspector-hidden', false);
90819                     inspectorWrap
90820                         .classed('inspector-hidden', true);
90821
90822                     if (_current) _current.remove();
90823                     _current = null;
90824                 };
90825
90826
90827                 sidebar.expand = function(moveMap) {
90828                     if (selection.classed('collapsed')) {
90829                         sidebar.toggle(moveMap);
90830                     }
90831                 };
90832
90833
90834                 sidebar.collapse = function(moveMap) {
90835                     if (!selection.classed('collapsed')) {
90836                         sidebar.toggle(moveMap);
90837                     }
90838                 };
90839
90840
90841                 sidebar.toggle = function(moveMap) {
90842                     var e = event;
90843                     if (e && e.sourceEvent) {
90844                         e.sourceEvent.preventDefault();
90845                     } else if (e) {
90846                         e.preventDefault();
90847                     }
90848
90849                     // Don't allow sidebar to toggle when the user is in the walkthrough.
90850                     if (context.inIntro()) return;
90851
90852                     var isCollapsed = selection.classed('collapsed');
90853                     var isCollapsing = !isCollapsed;
90854                     var isRTL = (_mainLocalizer.textDirection() === 'rtl');
90855                     var scaleX = isRTL ? 0 : 1;
90856                     var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
90857
90858                     sidebarWidth = selection.node().getBoundingClientRect().width;
90859
90860                     // switch from % to px
90861                     selection.style('width', sidebarWidth + 'px');
90862
90863                     var startMargin, endMargin, lastMargin;
90864                     if (isCollapsing) {
90865                         startMargin = lastMargin = 0;
90866                         endMargin = -sidebarWidth;
90867                     } else {
90868                         startMargin = lastMargin = -sidebarWidth;
90869                         endMargin = 0;
90870                     }
90871
90872                     selection.transition()
90873                         .style(xMarginProperty, endMargin + 'px')
90874                         .tween('panner', function() {
90875                             var i = d3_interpolateNumber(startMargin, endMargin);
90876                             return function(t) {
90877                                 var dx = lastMargin - Math.round(i(t));
90878                                 lastMargin = lastMargin - dx;
90879                                 context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]);
90880                             };
90881                         })
90882                         .on('end', function() {
90883                             selection.classed('collapsed', isCollapsing);
90884
90885                             // switch back from px to %
90886                             if (!isCollapsing) {
90887                                 var containerWidth = container.node().getBoundingClientRect().width;
90888                                 var widthPct = (sidebarWidth / containerWidth) * 100;
90889                                 selection
90890                                     .style(xMarginProperty, null)
90891                                     .style('width', widthPct + '%');
90892                             }
90893                         });
90894                 };
90895
90896                 // toggle the sidebar collapse when double-clicking the resizer
90897                 resizer.on('dblclick', sidebar.toggle);
90898
90899                 // ensure hover sidebar is closed when zooming out beyond editable zoom
90900                 context.map().on('crossEditableZoom.sidebar', function(within) {
90901                     if (!within && !selection.select('.inspector-hover').empty()) {
90902                         hover([]);
90903                     }
90904                 });
90905             }
90906
90907             sidebar.showPresetList = function() {};
90908             sidebar.hover = function() {};
90909             sidebar.hover.cancel = function() {};
90910             sidebar.intersects = function() {};
90911             sidebar.select = function() {};
90912             sidebar.show = function() {};
90913             sidebar.hide = function() {};
90914             sidebar.expand = function() {};
90915             sidebar.collapse = function() {};
90916             sidebar.toggle = function() {};
90917
90918             return sidebar;
90919         }
90920
90921         function uiSourceSwitch(context) {
90922             var keys;
90923
90924
90925             function click() {
90926                 event.preventDefault();
90927
90928                 var osm = context.connection();
90929                 if (!osm) return;
90930
90931                 if (context.inIntro()) return;
90932
90933                 if (context.history().hasChanges() &&
90934                     !window.confirm(_t('source_switch.lose_changes'))) return;
90935
90936                 var isLive = select(this)
90937                     .classed('live');
90938
90939                 isLive = !isLive;
90940                 context.enter(modeBrowse(context));
90941                 context.history().clearSaved();          // remove saved history
90942                 context.flush();                         // remove stored data
90943
90944                 select(this)
90945                     .text(isLive ? _t('source_switch.live') : _t('source_switch.dev'))
90946                     .classed('live', isLive)
90947                     .classed('chip', isLive);
90948
90949                 osm.switch(isLive ? keys[0] : keys[1]);  // switch connection (warning: dispatches 'change' event)
90950             }
90951
90952             var sourceSwitch = function(selection) {
90953                 selection
90954                     .append('a')
90955                     .attr('href', '#')
90956                     .text(_t('source_switch.live'))
90957                     .attr('class', 'live chip')
90958                     .on('click', click);
90959             };
90960
90961
90962             sourceSwitch.keys = function(_) {
90963                 if (!arguments.length) return keys;
90964                 keys = _;
90965                 return sourceSwitch;
90966             };
90967
90968
90969             return sourceSwitch;
90970         }
90971
90972         function uiSpinner(context) {
90973             var osm = context.connection();
90974
90975
90976             return function(selection) {
90977                 var img = selection
90978                     .append('img')
90979                     .attr('src', context.imagePath('loader-black.gif'))
90980                     .style('opacity', 0);
90981
90982                 if (osm) {
90983                     osm
90984                         .on('loading.spinner', function() {
90985                             img.transition()
90986                                 .style('opacity', 1);
90987                         })
90988                         .on('loaded.spinner', function() {
90989                             img.transition()
90990                                 .style('opacity', 0);
90991                         });
90992                 }
90993             };
90994         }
90995
90996         function uiSplash(context) {
90997           return (selection) => {
90998             // Exception - if there are restorable changes, skip this splash screen.
90999             // This is because we currently only support one `uiModal` at a time
91000             //  and we need to show them `uiRestore`` instead of this one.
91001             if (context.history().hasRestorableChanges()) return;
91002
91003             // If user has not seen this version of the privacy policy, show the splash again.
91004             let updateMessage = '';
91005             const sawPrivacyVersion = corePreferences('sawPrivacyVersion');
91006             let showSplash = !corePreferences('sawSplash');
91007             if (sawPrivacyVersion !== context.privacyVersion) {
91008               updateMessage = _t('splash.privacy_update');
91009               showSplash = true;
91010             }
91011
91012             if (!showSplash) return;
91013
91014             corePreferences('sawSplash', true);
91015             corePreferences('sawPrivacyVersion', context.privacyVersion);
91016
91017             // fetch intro graph data now, while user is looking at the splash screen
91018             _mainFileFetcher.get('intro_graph');
91019
91020             let modalSelection = uiModal(selection);
91021
91022             modalSelection.select('.modal')
91023               .attr('class', 'modal-splash modal');
91024
91025             let introModal = modalSelection.select('.content')
91026               .append('div')
91027               .attr('class', 'fillL');
91028
91029             introModal
91030               .append('div')
91031               .attr('class','modal-section')
91032               .append('h3')
91033               .text(_t('splash.welcome'));
91034
91035             let modalSection = introModal
91036               .append('div')
91037               .attr('class','modal-section');
91038
91039             modalSection
91040               .append('p')
91041               .html(_t('splash.text', {
91042                 version: context.version,
91043                 website: '<a target="_blank" href="http://ideditor.blog/">ideditor.blog</a>',
91044                 github: '<a target="_blank" href="https://github.com/openstreetmap/iD">github.com</a>'
91045               }));
91046
91047             modalSection
91048               .append('p')
91049               .html(_t('splash.privacy', {
91050                 updateMessage: updateMessage,
91051                 privacyLink: '<a target="_blank" href="https://github.com/openstreetmap/iD/blob/release/PRIVACY.md">' +
91052                   _t('splash.privacy_policy') + '</a>'
91053               }));
91054
91055             let buttonWrap = introModal
91056               .append('div')
91057               .attr('class', 'modal-actions');
91058
91059             let walkthrough = buttonWrap
91060               .append('button')
91061               .attr('class', 'walkthrough')
91062               .on('click', () => {
91063                 context.container().call(uiIntro(context));
91064                 modalSelection.close();
91065               });
91066
91067             walkthrough
91068               .append('svg')
91069               .attr('class', 'logo logo-walkthrough')
91070               .append('use')
91071               .attr('xlink:href', '#iD-logo-walkthrough');
91072
91073             walkthrough
91074               .append('div')
91075               .text(_t('splash.walkthrough'));
91076
91077             let startEditing = buttonWrap
91078               .append('button')
91079               .attr('class', 'start-editing')
91080               .on('click', modalSelection.close);
91081
91082             startEditing
91083               .append('svg')
91084               .attr('class', 'logo logo-features')
91085               .append('use')
91086               .attr('xlink:href', '#iD-logo-features');
91087
91088             startEditing
91089               .append('div')
91090               .text(_t('splash.start'));
91091
91092             modalSelection.select('button.close')
91093               .attr('class','hide');
91094           };
91095         }
91096
91097         function uiStatus(context) {
91098             var osm = context.connection();
91099
91100
91101             return function(selection) {
91102                 if (!osm) return;
91103
91104                 function update(err, apiStatus) {
91105                     selection.html('');
91106
91107                     if (err) {
91108                         if (apiStatus === 'connectionSwitched') {
91109                             // if the connection was just switched, we can't rely on
91110                             // the status (we're getting the status of the previous api)
91111                             return;
91112
91113                         } else if (apiStatus === 'rateLimited') {
91114                             selection
91115                                 .text(_t('osm_api_status.message.rateLimit'))
91116                                 .append('a')
91117                                 .attr('class', 'api-status-login')
91118                                 .attr('target', '_blank')
91119                                 .call(svgIcon('#iD-icon-out-link', 'inline'))
91120                                 .append('span')
91121                                 .text(_t('login'))
91122                                 .on('click.login', function() {
91123                                     event.preventDefault();
91124                                     osm.authenticate();
91125                                 });
91126                         } else {
91127
91128                             // don't allow retrying too rapidly
91129                             var throttledRetry = throttle(function() {
91130                                 // try loading the visible tiles
91131                                 context.loadTiles(context.projection);
91132                                 // manually reload the status too in case all visible tiles were already loaded
91133                                 osm.reloadApiStatus();
91134                             }, 2000);
91135
91136                             // eslint-disable-next-line no-warning-comments
91137                             // TODO: nice messages for different error types
91138                             selection
91139                                 .text(_t('osm_api_status.message.error') + ' ')
91140                                 .append('a')
91141                                 // let the user manually retry their connection directly
91142                                 .text(_t('osm_api_status.retry'))
91143                                 .on('click.retry', function() {
91144                                     event.preventDefault();
91145                                     throttledRetry();
91146                                 });
91147                         }
91148
91149                     } else if (apiStatus === 'readonly') {
91150                         selection.text(_t('osm_api_status.message.readonly'));
91151                     } else if (apiStatus === 'offline') {
91152                         selection.text(_t('osm_api_status.message.offline'));
91153                     }
91154
91155                     selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
91156                 }
91157
91158                 osm.on('apiStatusChange.uiStatus', update);
91159
91160                 // reload the status periodically regardless of other factors
91161                 window.setInterval(function() {
91162                     osm.reloadApiStatus();
91163                 }, 90000);
91164
91165                 // load the initial status in case no OSM data was loaded yet
91166                 osm.reloadApiStatus();
91167             };
91168         }
91169
91170         function modeDrawArea(context, wayID, startGraph, button) {
91171             var mode = {
91172                 button: button,
91173                 id: 'draw-area'
91174             };
91175
91176             var behavior = behaviorDrawWay(context, wayID, mode, startGraph)
91177                 .on('rejectedSelfIntersection.modeDrawArea', function() {
91178                     context.ui().flash
91179                         .text(_t('self_intersection.error.areas'))();
91180                 });
91181
91182             mode.wayID = wayID;
91183
91184             mode.enter = function() {
91185                 context.install(behavior);
91186             };
91187
91188             mode.exit = function() {
91189                 context.uninstall(behavior);
91190             };
91191
91192             mode.selectedIDs = function() {
91193                 return [wayID];
91194             };
91195
91196             mode.activeID = function() {
91197                 return (behavior && behavior.activeID()) || [];
91198             };
91199
91200             return mode;
91201         }
91202
91203         function modeAddArea(context, mode) {
91204             mode.id = 'add-area';
91205
91206             var behavior = behaviorAddWay(context)
91207                 .on('start', start)
91208                 .on('startFromWay', startFromWay)
91209                 .on('startFromNode', startFromNode);
91210
91211             var defaultTags = { area: 'yes' };
91212             if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'area');
91213
91214
91215             function actionClose(wayId) {
91216                 return function (graph) {
91217                     return graph.replace(graph.entity(wayId).close());
91218                 };
91219             }
91220
91221
91222             function start(loc) {
91223                 var startGraph = context.graph();
91224                 var node = osmNode({ loc: loc });
91225                 var way = osmWay({ tags: defaultTags });
91226
91227                 context.perform(
91228                     actionAddEntity(node),
91229                     actionAddEntity(way),
91230                     actionAddVertex(way.id, node.id),
91231                     actionClose(way.id)
91232                 );
91233
91234                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91235             }
91236
91237
91238             function startFromWay(loc, edge) {
91239                 var startGraph = context.graph();
91240                 var node = osmNode({ loc: loc });
91241                 var way = osmWay({ tags: defaultTags });
91242
91243                 context.perform(
91244                     actionAddEntity(node),
91245                     actionAddEntity(way),
91246                     actionAddVertex(way.id, node.id),
91247                     actionClose(way.id),
91248                     actionAddMidpoint({ loc: loc, edge: edge }, node)
91249                 );
91250
91251                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91252             }
91253
91254
91255             function startFromNode(node) {
91256                 var startGraph = context.graph();
91257                 var way = osmWay({ tags: defaultTags });
91258
91259                 context.perform(
91260                     actionAddEntity(way),
91261                     actionAddVertex(way.id, node.id),
91262                     actionClose(way.id)
91263                 );
91264
91265                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91266             }
91267
91268
91269             mode.enter = function() {
91270                 context.install(behavior);
91271             };
91272
91273
91274             mode.exit = function() {
91275                 context.uninstall(behavior);
91276             };
91277
91278
91279             return mode;
91280         }
91281
91282         function modeAddLine(context, mode) {
91283             mode.id = 'add-line';
91284
91285             var behavior = behaviorAddWay(context)
91286                 .on('start', start)
91287                 .on('startFromWay', startFromWay)
91288                 .on('startFromNode', startFromNode);
91289
91290             var defaultTags = {};
91291             if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'line');
91292
91293
91294             function start(loc) {
91295                 var startGraph = context.graph();
91296                 var node = osmNode({ loc: loc });
91297                 var way = osmWay({ tags: defaultTags });
91298
91299                 context.perform(
91300                     actionAddEntity(node),
91301                     actionAddEntity(way),
91302                     actionAddVertex(way.id, node.id)
91303                 );
91304
91305                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91306             }
91307
91308
91309             function startFromWay(loc, edge) {
91310                 var startGraph = context.graph();
91311                 var node = osmNode({ loc: loc });
91312                 var way = osmWay({ tags: defaultTags });
91313
91314                 context.perform(
91315                     actionAddEntity(node),
91316                     actionAddEntity(way),
91317                     actionAddVertex(way.id, node.id),
91318                     actionAddMidpoint({ loc: loc, edge: edge }, node)
91319                 );
91320
91321                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91322             }
91323
91324
91325             function startFromNode(node) {
91326                 var startGraph = context.graph();
91327                 var way = osmWay({ tags: defaultTags });
91328
91329                 context.perform(
91330                     actionAddEntity(way),
91331                     actionAddVertex(way.id, node.id)
91332                 );
91333
91334                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91335             }
91336
91337
91338             mode.enter = function() {
91339                 context.install(behavior);
91340             };
91341
91342
91343             mode.exit = function() {
91344                 context.uninstall(behavior);
91345             };
91346
91347             return mode;
91348         }
91349
91350         function modeAddPoint(context, mode) {
91351
91352             mode.id = 'add-point';
91353
91354             var behavior = behaviorDraw(context)
91355                 .on('click', add)
91356                 .on('clickWay', addWay)
91357                 .on('clickNode', addNode)
91358                 .on('cancel', cancel)
91359                 .on('finish', cancel);
91360
91361             var defaultTags = {};
91362             if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'point');
91363
91364
91365             function add(loc) {
91366                 var node = osmNode({ loc: loc, tags: defaultTags });
91367
91368                 context.perform(
91369                     actionAddEntity(node),
91370                     _t('operations.add.annotation.point')
91371                 );
91372
91373                 enterSelectMode(node);
91374             }
91375
91376
91377             function addWay(loc, edge) {
91378                 var node = osmNode({ tags: defaultTags });
91379
91380                 context.perform(
91381                     actionAddMidpoint({loc: loc, edge: edge}, node),
91382                     _t('operations.add.annotation.vertex')
91383                 );
91384
91385                 enterSelectMode(node);
91386             }
91387
91388             function enterSelectMode(node) {
91389                 context.enter(
91390                     modeSelect(context, [node.id]).newFeature(true)
91391                 );
91392             }
91393
91394
91395             function addNode(node) {
91396                 if (Object.keys(defaultTags).length === 0) {
91397                     enterSelectMode(node);
91398                     return;
91399                 }
91400
91401                 var tags = Object.assign({}, node.tags);  // shallow copy
91402                 for (var key in defaultTags) {
91403                     tags[key] = defaultTags[key];
91404                 }
91405
91406                 context.perform(
91407                     actionChangeTags(node.id, tags),
91408                     _t('operations.add.annotation.point')
91409                 );
91410
91411                 enterSelectMode(node);
91412             }
91413
91414
91415             function cancel() {
91416                 context.enter(modeBrowse(context));
91417             }
91418
91419
91420             mode.enter = function() {
91421                 context.install(behavior);
91422             };
91423
91424
91425             mode.exit = function() {
91426                 context.uninstall(behavior);
91427             };
91428
91429
91430             return mode;
91431         }
91432
91433         function modeAddNote(context) {
91434             var mode = {
91435                 id: 'add-note',
91436                 button: 'note',
91437                 title: _t('modes.add_note.title'),
91438                 description: _t('modes.add_note.description'),
91439                 key: _t('modes.add_note.key')
91440             };
91441
91442             var behavior = behaviorDraw(context)
91443                 .on('click', add)
91444                 .on('cancel', cancel)
91445                 .on('finish', cancel);
91446
91447
91448             function add(loc) {
91449                 var osm = services.osm;
91450                 if (!osm) return;
91451
91452                 var note = osmNote({ loc: loc, status: 'open', comments: [] });
91453                 osm.replaceNote(note);
91454
91455                 // force a reraw (there is no history change that would otherwise do this)
91456                 context.map().pan([0,0]);
91457
91458                 context
91459                     .selectedNoteID(note.id)
91460                     .enter(modeSelectNote(context, note.id).newFeature(true));
91461             }
91462
91463
91464             function cancel() {
91465                 context.enter(modeBrowse(context));
91466             }
91467
91468
91469             mode.enter = function() {
91470                 context.install(behavior);
91471             };
91472
91473
91474             mode.exit = function() {
91475                 context.uninstall(behavior);
91476             };
91477
91478
91479             return mode;
91480         }
91481
91482         function uiConflicts(context) {
91483             var dispatch$1 = dispatch('cancel', 'save');
91484             var keybinding = utilKeybinding('conflicts');
91485             var _origChanges;
91486             var _conflictList;
91487             var _shownConflictIndex;
91488
91489
91490             function keybindingOn() {
91491                 select(document)
91492                     .call(keybinding.on('⎋', cancel, true));
91493             }
91494
91495             function keybindingOff() {
91496                 select(document)
91497                     .call(keybinding.unbind);
91498             }
91499
91500             function tryAgain() {
91501                 keybindingOff();
91502                 dispatch$1.call('save');
91503             }
91504
91505             function cancel() {
91506                 keybindingOff();
91507                 dispatch$1.call('cancel');
91508             }
91509
91510
91511             function conflicts(selection) {
91512                 keybindingOn();
91513
91514                 var headerEnter = selection.selectAll('.header')
91515                     .data([0])
91516                     .enter()
91517                     .append('div')
91518                     .attr('class', 'header fillL');
91519
91520                 headerEnter
91521                     .append('button')
91522                     .attr('class', 'fr')
91523                     .on('click', cancel)
91524                     .call(svgIcon('#iD-icon-close'));
91525
91526                 headerEnter
91527                     .append('h3')
91528                     .text(_t('save.conflict.header'));
91529
91530                 var bodyEnter = selection.selectAll('.body')
91531                     .data([0])
91532                     .enter()
91533                     .append('div')
91534                     .attr('class', 'body fillL');
91535
91536                 var conflictsHelpEnter = bodyEnter
91537                     .append('div')
91538                     .attr('class', 'conflicts-help')
91539                     .text(_t('save.conflict.help'));
91540
91541
91542                 // Download changes link
91543                 var detected = utilDetect();
91544                 var changeset = new osmChangeset();
91545
91546                 delete changeset.id;  // Export without changeset_id
91547
91548                 var data = JXON.stringify(changeset.osmChangeJXON(_origChanges));
91549                 var blob = new Blob([data], { type: 'text/xml;charset=utf-8;' });
91550                 var fileName = 'changes.osc';
91551
91552                 var linkEnter = conflictsHelpEnter.selectAll('.download-changes')
91553                     .append('a')
91554                     .attr('class', 'download-changes');
91555
91556                 if (detected.download) {      // All except IE11 and Edge
91557                     linkEnter                 // download the data as a file
91558                         .attr('href', window.URL.createObjectURL(blob))
91559                         .attr('download', fileName);
91560
91561                 } else {                      // IE11 and Edge
91562                     linkEnter                 // open data uri in a new tab
91563                         .attr('target', '_blank')
91564                         .on('click.download', function() {
91565                             navigator.msSaveBlob(blob, fileName);
91566                         });
91567                 }
91568
91569                 linkEnter
91570                     .call(svgIcon('#iD-icon-load', 'inline'))
91571                     .append('span')
91572                     .text(_t('save.conflict.download_changes'));
91573
91574
91575                 bodyEnter
91576                     .append('div')
91577                     .attr('class', 'conflict-container fillL3')
91578                     .call(showConflict, 0);
91579
91580                 bodyEnter
91581                     .append('div')
91582                     .attr('class', 'conflicts-done')
91583                     .attr('opacity', 0)
91584                     .style('display', 'none')
91585                     .text(_t('save.conflict.done'));
91586
91587                 var buttonsEnter = bodyEnter
91588                     .append('div')
91589                     .attr('class','buttons col12 joined conflicts-buttons');
91590
91591                 buttonsEnter
91592                     .append('button')
91593                     .attr('disabled', _conflictList.length > 1)
91594                     .attr('class', 'action conflicts-button col6')
91595                     .text(_t('save.title'))
91596                     .on('click.try_again', tryAgain);
91597
91598                 buttonsEnter
91599                     .append('button')
91600                     .attr('class', 'secondary-action conflicts-button col6')
91601                     .text(_t('confirm.cancel'))
91602                     .on('click.cancel', cancel);
91603             }
91604
91605
91606             function showConflict(selection, index) {
91607                 index = utilWrap(index, _conflictList.length);
91608                 _shownConflictIndex = index;
91609
91610                 var parent = select(selection.node().parentNode);
91611
91612                 // enable save button if this is the last conflict being reviewed..
91613                 if (index === _conflictList.length - 1) {
91614                     window.setTimeout(function() {
91615                         parent.select('.conflicts-button')
91616                             .attr('disabled', null);
91617
91618                         parent.select('.conflicts-done')
91619                             .transition()
91620                             .attr('opacity', 1)
91621                             .style('display', 'block');
91622                     }, 250);
91623                 }
91624
91625                 var conflict = selection
91626                     .selectAll('.conflict')
91627                     .data([_conflictList[index]]);
91628
91629                 conflict.exit()
91630                     .remove();
91631
91632                 var conflictEnter = conflict.enter()
91633                     .append('div')
91634                     .attr('class', 'conflict');
91635
91636                 conflictEnter
91637                     .append('h4')
91638                     .attr('class', 'conflict-count')
91639                     .text(_t('save.conflict.count', { num: index + 1, total: _conflictList.length }));
91640
91641                 conflictEnter
91642                     .append('a')
91643                     .attr('class', 'conflict-description')
91644                     .attr('href', '#')
91645                     .text(function(d) { return d.name; })
91646                     .on('click', function(d) {
91647                         event.preventDefault();
91648                         zoomToEntity(d.id);
91649                     });
91650
91651                 var details = conflictEnter
91652                     .append('div')
91653                     .attr('class', 'conflict-detail-container');
91654
91655                 details
91656                     .append('ul')
91657                     .attr('class', 'conflict-detail-list')
91658                     .selectAll('li')
91659                     .data(function(d) { return d.details || []; })
91660                     .enter()
91661                     .append('li')
91662                     .attr('class', 'conflict-detail-item')
91663                     .html(function(d) { return d; });
91664
91665                 details
91666                     .append('div')
91667                     .attr('class', 'conflict-choices')
91668                     .call(addChoices);
91669
91670                 details
91671                     .append('div')
91672                     .attr('class', 'conflict-nav-buttons joined cf')
91673                     .selectAll('button')
91674                     .data(['previous', 'next'])
91675                     .enter()
91676                     .append('button')
91677                     .text(function(d) { return _t('save.conflict.' + d); })
91678                     .attr('class', 'conflict-nav-button action col6')
91679                     .attr('disabled', function(d, i) {
91680                         return (i === 0 && index === 0) ||
91681                             (i === 1 && index === _conflictList.length - 1) || null;
91682                     })
91683                     .on('click', function(d, i) {
91684                         event.preventDefault();
91685
91686                         var container = parent.selectAll('.conflict-container');
91687                         var sign = (i === 0 ? -1 : 1);
91688
91689                         container
91690                             .selectAll('.conflict')
91691                             .remove();
91692
91693                         container
91694                             .call(showConflict, index + sign);
91695                     });
91696
91697             }
91698
91699
91700             function addChoices(selection) {
91701                 var choices = selection
91702                     .append('ul')
91703                     .attr('class', 'layer-list')
91704                     .selectAll('li')
91705                     .data(function(d) { return d.choices || []; });
91706
91707                 // enter
91708                 var choicesEnter = choices.enter()
91709                     .append('li')
91710                     .attr('class', 'layer');
91711
91712                 var labelEnter = choicesEnter
91713                     .append('label');
91714
91715                 labelEnter
91716                     .append('input')
91717                     .attr('type', 'radio')
91718                     .attr('name', function(d) { return d.id; })
91719                     .on('change', function(d, i) {
91720                         var ul = this.parentNode.parentNode.parentNode;
91721                         ul.__data__.chosen = i;
91722                         choose(ul, d);
91723                     });
91724
91725                 labelEnter
91726                     .append('span')
91727                     .text(function(d) { return d.text; });
91728
91729                 // update
91730                 choicesEnter
91731                     .merge(choices)
91732                     .each(function(d, i) {
91733                         var ul = this.parentNode;
91734                         if (ul.__data__.chosen === i) {
91735                             choose(ul, d);
91736                         }
91737                     });
91738             }
91739
91740
91741             function choose(ul, datum) {
91742                 if (event) event.preventDefault();
91743
91744                 select(ul)
91745                     .selectAll('li')
91746                     .classed('active', function(d) { return d === datum; })
91747                     .selectAll('input')
91748                     .property('checked', function(d) { return d === datum; });
91749
91750                 var extent = geoExtent();
91751                 var entity;
91752
91753                 entity = context.graph().hasEntity(datum.id);
91754                 if (entity) extent._extend(entity.extent(context.graph()));
91755
91756                 datum.action();
91757
91758                 entity = context.graph().hasEntity(datum.id);
91759                 if (entity) extent._extend(entity.extent(context.graph()));
91760
91761                 zoomToEntity(datum.id, extent);
91762             }
91763
91764
91765             function zoomToEntity(id, extent) {
91766                 context.surface().selectAll('.hover')
91767                     .classed('hover', false);
91768
91769                 var entity = context.graph().hasEntity(id);
91770                 if (entity) {
91771                     if (extent) {
91772                         context.map().trimmedExtent(extent);
91773                     } else {
91774                         context.map().zoomToEase(entity);
91775                     }
91776                     context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph()))
91777                         .classed('hover', true);
91778                 }
91779             }
91780
91781
91782             // The conflict list should be an array of objects like:
91783             // {
91784             //     id: id,
91785             //     name: entityName(local),
91786             //     details: merge.conflicts(),
91787             //     chosen: 1,
91788             //     choices: [
91789             //         choice(id, keepMine, forceLocal),
91790             //         choice(id, keepTheirs, forceRemote)
91791             //     ]
91792             // }
91793             conflicts.conflictList = function(_) {
91794                 if (!arguments.length) return _conflictList;
91795                 _conflictList = _;
91796                 return conflicts;
91797             };
91798
91799
91800             conflicts.origChanges = function(_) {
91801                 if (!arguments.length) return _origChanges;
91802                 _origChanges = _;
91803                 return conflicts;
91804             };
91805
91806
91807             conflicts.shownEntityIds = function() {
91808                 if (_conflictList && typeof _shownConflictIndex === 'number') {
91809                     return [_conflictList[_shownConflictIndex].id];
91810                 }
91811                 return [];
91812             };
91813
91814
91815             return utilRebind(conflicts, dispatch$1, 'on');
91816         }
91817
91818         function uiConfirm(selection) {
91819             var modalSelection = uiModal(selection);
91820
91821             modalSelection.select('.modal')
91822                 .classed('modal-alert', true);
91823
91824             var section = modalSelection.select('.content');
91825
91826             section.append('div')
91827                 .attr('class', 'modal-section header');
91828
91829             section.append('div')
91830                 .attr('class', 'modal-section message-text');
91831
91832             var buttons = section.append('div')
91833                 .attr('class', 'modal-section buttons cf');
91834
91835
91836             modalSelection.okButton = function() {
91837                 buttons
91838                     .append('button')
91839                     .attr('class', 'button ok-button action')
91840                     .on('click.confirm', function() {
91841                         modalSelection.remove();
91842                     })
91843                     .text(_t('confirm.okay'))
91844                     .node()
91845                     .focus();
91846
91847                 return modalSelection;
91848             };
91849
91850
91851             return modalSelection;
91852         }
91853
91854         function uiChangesetEditor(context) {
91855             var dispatch$1 = dispatch('change');
91856             var formFields = uiFormFields(context);
91857             var commentCombo = uiCombobox(context, 'comment').caseSensitive(true);
91858             var _fieldsArr;
91859             var _tags;
91860             var _changesetID;
91861
91862
91863             function changesetEditor(selection) {
91864                 render(selection);
91865             }
91866
91867
91868             function render(selection) {
91869                 var initial = false;
91870
91871                 if (!_fieldsArr) {
91872                     initial = true;
91873                     var presets = _mainPresetIndex;
91874
91875                     _fieldsArr = [
91876                         uiField(context, presets.field('comment'), null, { show: true, revert: false }),
91877                         uiField(context, presets.field('source'), null, { show: false, revert: false }),
91878                         uiField(context, presets.field('hashtags'), null, { show: false, revert: false }),
91879                     ];
91880
91881                     _fieldsArr.forEach(function(field) {
91882                         field
91883                             .on('change', function(t, onInput) {
91884                                 dispatch$1.call('change', field, undefined, t, onInput);
91885                             });
91886                     });
91887                 }
91888
91889                 _fieldsArr.forEach(function(field) {
91890                     field
91891                         .tags(_tags);
91892                 });
91893
91894
91895                 selection
91896                     .call(formFields.fieldsArr(_fieldsArr));
91897
91898
91899                 if (initial) {
91900                     var commentField = selection.select('.form-field-comment textarea');
91901                     var commentNode = commentField.node();
91902
91903                     if (commentNode) {
91904                         commentNode.focus();
91905                         commentNode.select();
91906                     }
91907
91908                     // trigger a 'blur' event so that comment field can be cleaned
91909                     // and checked for hashtags, even if retrieved from localstorage
91910                     utilTriggerEvent(commentField, 'blur');
91911
91912                     var osm = context.connection();
91913                     if (osm) {
91914                         osm.userChangesets(function (err, changesets) {
91915                             if (err) return;
91916
91917                             var comments = changesets.map(function(changeset) {
91918                                 var comment = changeset.tags.comment;
91919                                 return comment ? { title: comment, value: comment } : null;
91920                             }).filter(Boolean);
91921
91922                             commentField
91923                                 .call(commentCombo
91924                                     .data(utilArrayUniqBy(comments, 'title'))
91925                                 );
91926                         });
91927                     }
91928                 }
91929
91930                 // Add warning if comment mentions Google
91931                 var hasGoogle = _tags.comment.match(/google/i);
91932                 var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning')
91933                     .data(hasGoogle ? [0] : []);
91934
91935                 commentWarning.exit()
91936                     .transition()
91937                     .duration(200)
91938                     .style('opacity', 0)
91939                     .remove();
91940
91941                 var commentEnter = commentWarning.enter()
91942                     .insert('div', '.tag-reference-body')
91943                     .attr('class', 'field-warning comment-warning')
91944                     .style('opacity', 0);
91945
91946                 commentEnter
91947                     .append('a')
91948                     .attr('target', '_blank')
91949                     .attr('tabindex', -1)
91950                     .call(svgIcon('#iD-icon-alert', 'inline'))
91951                     .attr('href', _t('commit.google_warning_link'))
91952                     .append('span')
91953                     .text(_t('commit.google_warning'));
91954
91955                 commentEnter
91956                     .transition()
91957                     .duration(200)
91958                     .style('opacity', 1);
91959             }
91960
91961
91962             changesetEditor.tags = function(_) {
91963                 if (!arguments.length) return _tags;
91964                 _tags = _;
91965                 // Don't reset _fieldsArr here.
91966                 return changesetEditor;
91967             };
91968
91969
91970             changesetEditor.changesetID = function(_) {
91971                 if (!arguments.length) return _changesetID;
91972                 if (_changesetID === _) return changesetEditor;
91973                 _changesetID = _;
91974                 _fieldsArr = null;
91975                 return changesetEditor;
91976             };
91977
91978
91979             return utilRebind(changesetEditor, dispatch$1, 'on');
91980         }
91981
91982         function uiSectionChanges(context) {
91983             var detected = utilDetect();
91984
91985             var _discardTags = {};
91986             _mainFileFetcher.get('discarded')
91987                 .then(function(d) { _discardTags = d; })
91988                 .catch(function() { /* ignore */ });
91989
91990             var section = uiSection('changes-list', context)
91991                 .title(function() {
91992                     var history = context.history();
91993                     var summary = history.difference().summary();
91994                     return _t('commit.changes', { count: summary.length });
91995                 })
91996                 .disclosureContent(renderDisclosureContent);
91997
91998             function renderDisclosureContent(selection) {
91999                 var history = context.history();
92000                 var summary = history.difference().summary();
92001
92002                 var container = selection.selectAll('.commit-section')
92003                     .data([0]);
92004
92005                 var containerEnter = container.enter()
92006                     .append('div')
92007                     .attr('class', 'commit-section');
92008
92009                 containerEnter
92010                     .append('ul')
92011                     .attr('class', 'changeset-list');
92012
92013                 container = containerEnter
92014                     .merge(container);
92015
92016
92017                 var items = container.select('ul').selectAll('li')
92018                     .data(summary);
92019
92020                 var itemsEnter = items.enter()
92021                     .append('li')
92022                     .attr('class', 'change-item');
92023
92024                 itemsEnter
92025                     .each(function(d) {
92026                         select(this)
92027                             .call(svgIcon('#iD-icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
92028                     });
92029
92030                 itemsEnter
92031                     .append('span')
92032                     .attr('class', 'change-type')
92033                     .text(function(d) { return _t('commit.' + d.changeType) + ' '; });
92034
92035                 itemsEnter
92036                     .append('strong')
92037                     .attr('class', 'entity-type')
92038                     .text(function(d) {
92039                         var matched = _mainPresetIndex.match(d.entity, d.graph);
92040                         return (matched && matched.name()) || utilDisplayType(d.entity.id);
92041                     });
92042
92043                 itemsEnter
92044                     .append('span')
92045                     .attr('class', 'entity-name')
92046                     .text(function(d) {
92047                         var name = utilDisplayName(d.entity) || '',
92048                             string = '';
92049                         if (name !== '') {
92050                             string += ':';
92051                         }
92052                         return string += ' ' + name;
92053                     });
92054
92055                 itemsEnter
92056                     .style('opacity', 0)
92057                     .transition()
92058                     .style('opacity', 1);
92059
92060                 items = itemsEnter
92061                     .merge(items);
92062
92063                 items
92064                     .on('mouseover', mouseover)
92065                     .on('mouseout', mouseout)
92066                     .on('click', click);
92067
92068
92069                 // Download changeset link
92070                 var changeset = new osmChangeset().update({ id: undefined });
92071                 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
92072
92073                 delete changeset.id;  // Export without chnageset_id
92074
92075                 var data = JXON.stringify(changeset.osmChangeJXON(changes));
92076                 var blob = new Blob([data], {type: 'text/xml;charset=utf-8;'});
92077                 var fileName = 'changes.osc';
92078
92079                 var linkEnter = container.selectAll('.download-changes')
92080                     .data([0])
92081                     .enter()
92082                     .append('a')
92083                     .attr('class', 'download-changes');
92084
92085                 if (detected.download) {      // All except IE11 and Edge
92086                     linkEnter                 // download the data as a file
92087                         .attr('href', window.URL.createObjectURL(blob))
92088                         .attr('download', fileName);
92089
92090                 } else {                      // IE11 and Edge
92091                     linkEnter                 // open data uri in a new tab
92092                         .attr('target', '_blank')
92093                         .on('click.download', function() {
92094                             navigator.msSaveBlob(blob, fileName);
92095                         });
92096                 }
92097
92098                 linkEnter
92099                     .call(svgIcon('#iD-icon-load', 'inline'))
92100                     .append('span')
92101                     .text(_t('commit.download_changes'));
92102
92103
92104                 function mouseover(d) {
92105                     if (d.entity) {
92106                         context.surface().selectAll(
92107                             utilEntityOrMemberSelector([d.entity.id], context.graph())
92108                         ).classed('hover', true);
92109                     }
92110                 }
92111
92112
92113                 function mouseout() {
92114                     context.surface().selectAll('.hover')
92115                         .classed('hover', false);
92116                 }
92117
92118
92119                 function click(change) {
92120                     if (change.changeType !== 'deleted') {
92121                         var entity = change.entity;
92122                         context.map().zoomToEase(entity);
92123                         context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph()))
92124                             .classed('hover', true);
92125                     }
92126                 }
92127             }
92128
92129             return section;
92130         }
92131
92132         function uiCommitWarnings(context) {
92133
92134             function commitWarnings(selection) {
92135                 var issuesBySeverity = context.validator()
92136                     .getIssuesBySeverity({ what: 'edited', where: 'all', includeDisabledRules: true });
92137
92138                 for (var severity in issuesBySeverity) {
92139                     var issues = issuesBySeverity[severity];
92140                     var section = severity + '-section';
92141                     var issueItem = severity + '-item';
92142
92143                     var container = selection.selectAll('.' + section)
92144                         .data(issues.length ? [0] : []);
92145
92146                     container.exit()
92147                         .remove();
92148
92149                     var containerEnter = container.enter()
92150                         .append('div')
92151                         .attr('class', 'modal-section ' + section + ' fillL2');
92152
92153                     containerEnter
92154                         .append('h3')
92155                         .text(severity === 'warning' ? _t('commit.warnings') : _t('commit.errors'));
92156
92157                     containerEnter
92158                         .append('ul')
92159                         .attr('class', 'changeset-list');
92160
92161                     container = containerEnter
92162                         .merge(container);
92163
92164
92165                     var items = container.select('ul').selectAll('li')
92166                         .data(issues, function(d) { return d.id; });
92167
92168                     items.exit()
92169                         .remove();
92170
92171                     var itemsEnter = items.enter()
92172                         .append('li')
92173                         .attr('class', issueItem);
92174
92175                     itemsEnter
92176                         .call(svgIcon('#iD-icon-alert', 'pre-text'));
92177
92178                     itemsEnter
92179                         .append('strong')
92180                         .attr('class', 'issue-message');
92181
92182                     itemsEnter.filter(function(d) { return d.tooltip; })
92183                         .call(uiTooltip()
92184                             .title(function(d) { return d.tooltip; })
92185                             .placement('top')
92186                         );
92187
92188                     items = itemsEnter
92189                         .merge(items);
92190
92191                     items.selectAll('.issue-message')
92192                         .text(function(d) {
92193                             return d.message(context);
92194                         });
92195
92196                     items
92197                         .on('mouseover', function(d) {
92198                             if (d.entityIds) {
92199                                 context.surface().selectAll(
92200                                     utilEntityOrMemberSelector(
92201                                         d.entityIds,
92202                                         context.graph()
92203                                     )
92204                                 ).classed('hover', true);
92205                             }
92206                         })
92207                         .on('mouseout', function() {
92208                             context.surface().selectAll('.hover')
92209                                 .classed('hover', false);
92210                         })
92211                         .on('click', function(d) {
92212                             context.validator().focusIssue(d);
92213                         });
92214                 }
92215             }
92216
92217
92218             return commitWarnings;
92219         }
92220
92221         var readOnlyTags = [
92222             /^changesets_count$/,
92223             /^created_by$/,
92224             /^ideditor:/,
92225             /^imagery_used$/,
92226             /^host$/,
92227             /^locale$/,
92228             /^warnings:/,
92229             /^resolved:/,
92230             /^closed:note$/,
92231             /^closed:keepright$/,
92232             /^closed:improveosm:/,
92233             /^closed:osmose:/
92234         ];
92235
92236         // treat most punctuation (except -, _, +, &) as hashtag delimiters - #4398
92237         // from https://stackoverflow.com/a/25575009
92238         var hashtagRegex = /(#[^\u2000-\u206F\u2E00-\u2E7F\s\\'!"#$%()*,.\/:;<=>?@\[\]^`{|}~]+)/g;
92239
92240
92241         function uiCommit(context) {
92242             var dispatch$1 = dispatch('cancel');
92243             var _userDetails;
92244             var _selection;
92245
92246             var changesetEditor = uiChangesetEditor(context)
92247                 .on('change', changeTags);
92248             var rawTagEditor = uiSectionRawTagEditor('changeset-tag-editor', context)
92249                 .on('change', changeTags)
92250                 .readOnlyTags(readOnlyTags);
92251             var commitChanges = uiSectionChanges(context);
92252             var commitWarnings = uiCommitWarnings(context);
92253
92254
92255             function commit(selection) {
92256                 _selection = selection;
92257
92258                 // Initialize changeset if one does not exist yet.
92259                 if (!context.changeset) initChangeset();
92260
92261                 loadDerivedChangesetTags();
92262
92263                 selection.call(render);
92264             }
92265
92266             function initChangeset() {
92267
92268                 // expire stored comment, hashtags, source after cutoff datetime - #3947 #4899
92269                 var commentDate = +corePreferences('commentDate') || 0;
92270                 var currDate = Date.now();
92271                 var cutoff = 2 * 86400 * 1000;   // 2 days
92272                 if (commentDate > currDate || currDate - commentDate > cutoff) {
92273                     corePreferences('comment', null);
92274                     corePreferences('hashtags', null);
92275                     corePreferences('source', null);
92276                 }
92277
92278                 // load in explicitly-set values, if any
92279                 if (context.defaultChangesetComment()) {
92280                     corePreferences('comment', context.defaultChangesetComment());
92281                     corePreferences('commentDate', Date.now());
92282                 }
92283                 if (context.defaultChangesetSource()) {
92284                     corePreferences('source', context.defaultChangesetSource());
92285                     corePreferences('commentDate', Date.now());
92286                 }
92287                 if (context.defaultChangesetHashtags()) {
92288                     corePreferences('hashtags', context.defaultChangesetHashtags());
92289                     corePreferences('commentDate', Date.now());
92290                 }
92291
92292                 var detected = utilDetect();
92293                 var tags = {
92294                     comment: corePreferences('comment') || '',
92295                     created_by: context.cleanTagValue('iD ' + context.version),
92296                     host: context.cleanTagValue(detected.host),
92297                     locale: context.cleanTagValue(_mainLocalizer.localeCode())
92298                 };
92299
92300                 // call findHashtags initially - this will remove stored
92301                 // hashtags if any hashtags are found in the comment - #4304
92302                 findHashtags(tags, true);
92303
92304                 var hashtags = corePreferences('hashtags');
92305                 if (hashtags) {
92306                     tags.hashtags = hashtags;
92307                 }
92308
92309                 var source = corePreferences('source');
92310                 if (source) {
92311                     tags.source = source;
92312                 }
92313                 var photoOverlaysUsed = context.history().photoOverlaysUsed();
92314                 if (photoOverlaysUsed.length) {
92315                     var sources = (tags.source || '').split(';');
92316
92317                     // include this tag for any photo layer
92318                     if (sources.indexOf('streetlevel imagery') === -1) {
92319                         sources.push('streetlevel imagery');
92320                     }
92321
92322                     // add the photo overlays used during editing as sources
92323                     photoOverlaysUsed.forEach(function(photoOverlay) {
92324                         if (sources.indexOf(photoOverlay) === -1) {
92325                             sources.push(photoOverlay);
92326                         }
92327                     });
92328
92329                     tags.source = context.cleanTagValue(sources.join(';'));
92330                 }
92331
92332                 context.changeset = new osmChangeset({ tags: tags });
92333             }
92334
92335             // Calculates read-only metadata tags based on the user's editing session and applies
92336             // them to the changeset.
92337             function loadDerivedChangesetTags() {
92338
92339                 var osm = context.connection();
92340                 if (!osm) return;
92341
92342                 var tags = Object.assign({}, context.changeset.tags);   // shallow copy
92343
92344                 // assign tags for imagery used
92345                 var imageryUsed = context.cleanTagValue(context.history().imageryUsed().join(';'));
92346                 tags.imagery_used = imageryUsed || 'None';
92347
92348                 // assign tags for closed issues and notes
92349                 var osmClosed = osm.getClosedIDs();
92350                 var itemType;
92351                 if (osmClosed.length) {
92352                     tags['closed:note'] = context.cleanTagValue(osmClosed.join(';'));
92353                 }
92354                 if (services.keepRight) {
92355                     var krClosed = services.keepRight.getClosedIDs();
92356                     if (krClosed.length) {
92357                         tags['closed:keepright'] = context.cleanTagValue(krClosed.join(';'));
92358                     }
92359                 }
92360                 if (services.improveOSM) {
92361                     var iOsmClosed = services.improveOSM.getClosedCounts();
92362                     for (itemType in iOsmClosed) {
92363                         tags['closed:improveosm:' + itemType] = context.cleanTagValue(iOsmClosed[itemType].toString());
92364                     }
92365                 }
92366                 if (services.osmose) {
92367                     var osmoseClosed = services.osmose.getClosedCounts();
92368                     for (itemType in osmoseClosed) {
92369                         tags['closed:osmose:' + itemType] = context.cleanTagValue(osmoseClosed[itemType].toString());
92370                     }
92371                 }
92372
92373                 // remove existing issue counts
92374                 for (var key in tags) {
92375                     if (key.match(/(^warnings:)|(^resolved:)/)) {
92376                         delete tags[key];
92377                     }
92378                 }
92379
92380                 function addIssueCounts(issues, prefix) {
92381                     var issuesByType = utilArrayGroupBy(issues, 'type');
92382                     for (var issueType in issuesByType) {
92383                         var issuesOfType = issuesByType[issueType];
92384                         if (issuesOfType[0].subtype) {
92385                             var issuesBySubtype = utilArrayGroupBy(issuesOfType, 'subtype');
92386                             for (var issueSubtype in issuesBySubtype) {
92387                                 var issuesOfSubtype = issuesBySubtype[issueSubtype];
92388                                 tags[prefix + ':' + issueType + ':' + issueSubtype] = context.cleanTagValue(issuesOfSubtype.length.toString());
92389                             }
92390                         } else {
92391                             tags[prefix + ':' + issueType] = context.cleanTagValue(issuesOfType.length.toString());
92392                         }
92393                     }
92394                 }
92395
92396                 // add counts of warnings generated by the user's edits
92397                 var warnings = context.validator()
92398                     .getIssuesBySeverity({ what: 'edited', where: 'all', includeIgnored: true, includeDisabledRules: true }).warning;
92399                 addIssueCounts(warnings, 'warnings');
92400
92401                 // add counts of issues resolved by the user's edits
92402                 var resolvedIssues = context.validator().getResolvedIssues();
92403                 addIssueCounts(resolvedIssues, 'resolved');
92404
92405                 context.changeset = context.changeset.update({ tags: tags });
92406             }
92407
92408             function render(selection) {
92409
92410                 var osm = context.connection();
92411                 if (!osm) return;
92412
92413                 var header = selection.selectAll('.header')
92414                     .data([0]);
92415
92416                 var headerTitle = header.enter()
92417                     .append('div')
92418                     .attr('class', 'header fillL header-container');
92419
92420                 headerTitle
92421                     .append('div')
92422                     .attr('class', 'header-block header-block-outer');
92423
92424                 headerTitle
92425                     .append('div')
92426                     .attr('class', 'header-block')
92427                     .append('h3')
92428                     .text(_t('commit.title'));
92429
92430                 headerTitle
92431                     .append('div')
92432                     .attr('class', 'header-block header-block-outer header-block-close')
92433                     .append('button')
92434                     .attr('class', 'close')
92435                     .on('click', function() {
92436                         dispatch$1.call('cancel', this);
92437                     })
92438                     .call(svgIcon('#iD-icon-close'));
92439
92440                 var body = selection.selectAll('.body')
92441                     .data([0]);
92442
92443                 body = body.enter()
92444                     .append('div')
92445                     .attr('class', 'body')
92446                     .merge(body);
92447
92448
92449                 // Changeset Section
92450                 var changesetSection = body.selectAll('.changeset-editor')
92451                     .data([0]);
92452
92453                 changesetSection = changesetSection.enter()
92454                     .append('div')
92455                     .attr('class', 'modal-section changeset-editor')
92456                     .merge(changesetSection);
92457
92458                 changesetSection
92459                     .call(changesetEditor
92460                         .changesetID(context.changeset.id)
92461                         .tags(context.changeset.tags)
92462                     );
92463
92464
92465                 // Warnings
92466                 body.call(commitWarnings);
92467
92468
92469                 // Upload Explanation
92470                 var saveSection = body.selectAll('.save-section')
92471                     .data([0]);
92472
92473                 saveSection = saveSection.enter()
92474                     .append('div')
92475                     .attr('class','modal-section save-section fillL')
92476                     .merge(saveSection);
92477
92478                 var prose = saveSection.selectAll('.commit-info')
92479                     .data([0]);
92480
92481                 if (prose.enter().size()) {   // first time, make sure to update user details in prose
92482                     _userDetails = null;
92483                 }
92484
92485                 prose = prose.enter()
92486                     .append('p')
92487                     .attr('class', 'commit-info')
92488                     .text(_t('commit.upload_explanation'))
92489                     .merge(prose);
92490
92491                 // always check if this has changed, but only update prose.html()
92492                 // if needed, because it can trigger a style recalculation
92493                 osm.userDetails(function(err, user) {
92494                     if (err) return;
92495
92496                     if (_userDetails === user) return;  // no change
92497                     _userDetails = user;
92498
92499                     var userLink = select(document.createElement('div'));
92500
92501                     if (user.image_url) {
92502                         userLink
92503                             .append('img')
92504                             .attr('src', user.image_url)
92505                             .attr('class', 'icon pre-text user-icon');
92506                     }
92507
92508                     userLink
92509                         .append('a')
92510                         .attr('class', 'user-info')
92511                         .text(user.display_name)
92512                         .attr('href', osm.userURL(user.display_name))
92513                         .attr('target', '_blank');
92514
92515                     prose
92516                         .html(_t('commit.upload_explanation_with_user', { user: userLink.html() }));
92517                 });
92518
92519
92520                 // Request Review
92521                 var requestReview = saveSection.selectAll('.request-review')
92522                     .data([0]);
92523
92524                 // Enter
92525                 var requestReviewEnter = requestReview.enter()
92526                     .append('div')
92527                     .attr('class', 'request-review');
92528
92529                 var requestReviewDomId = utilUniqueDomId('commit-input-request-review');
92530
92531                 var labelEnter = requestReviewEnter
92532                     .append('label')
92533                     .attr('for', requestReviewDomId);
92534
92535                 labelEnter
92536                     .append('input')
92537                     .attr('type', 'checkbox')
92538                     .attr('id', requestReviewDomId);
92539
92540                 labelEnter
92541                     .append('span')
92542                     .text(_t('commit.request_review'));
92543
92544                 // Update
92545                 requestReview = requestReview
92546                     .merge(requestReviewEnter);
92547
92548                 var requestReviewInput = requestReview.selectAll('input')
92549                     .property('checked', isReviewRequested(context.changeset.tags))
92550                     .on('change', toggleRequestReview);
92551
92552
92553                 // Buttons
92554                 var buttonSection = saveSection.selectAll('.buttons')
92555                     .data([0]);
92556
92557                 // enter
92558                 var buttonEnter = buttonSection.enter()
92559                     .append('div')
92560                     .attr('class', 'buttons fillL');
92561
92562                 buttonEnter
92563                     .append('button')
92564                     .attr('class', 'secondary-action button cancel-button')
92565                     .append('span')
92566                     .attr('class', 'label')
92567                     .text(_t('commit.cancel'));
92568
92569                 var uploadButton = buttonEnter
92570                     .append('button')
92571                     .attr('class', 'action button save-button');
92572
92573                 uploadButton.append('span')
92574                     .attr('class', 'label')
92575                     .text(_t('commit.save'));
92576
92577                 var uploadBlockerTooltipText = getUploadBlockerMessage();
92578
92579                 // update
92580                 buttonSection = buttonSection
92581                     .merge(buttonEnter);
92582
92583                 buttonSection.selectAll('.cancel-button')
92584                     .on('click.cancel', function() {
92585                         dispatch$1.call('cancel', this);
92586                     });
92587
92588                 buttonSection.selectAll('.save-button')
92589                     .classed('disabled', uploadBlockerTooltipText !== null)
92590                     .on('click.save', function() {
92591                         if (!select(this).classed('disabled')) {
92592                             this.blur();    // avoid keeping focus on the button - #4641
92593                             context.uploader().save(context.changeset);
92594                         }
92595                     });
92596
92597                 // remove any existing tooltip
92598                 uiTooltip().destroyAny(buttonSection.selectAll('.save-button'));
92599
92600                 if (uploadBlockerTooltipText) {
92601                     buttonSection.selectAll('.save-button')
92602                         .call(uiTooltip().title(uploadBlockerTooltipText).placement('top'));
92603                 }
92604
92605                 // Raw Tag Editor
92606                 var tagSection = body.selectAll('.tag-section.raw-tag-editor')
92607                     .data([0]);
92608
92609                 tagSection = tagSection.enter()
92610                     .append('div')
92611                     .attr('class', 'modal-section tag-section raw-tag-editor')
92612                     .merge(tagSection);
92613
92614                 tagSection
92615                     .call(rawTagEditor
92616                         .tags(Object.assign({}, context.changeset.tags))   // shallow copy
92617                         .render
92618                     );
92619
92620                 var changesSection = body.selectAll('.commit-changes-section')
92621                     .data([0]);
92622
92623                 changesSection = changesSection.enter()
92624                     .append('div')
92625                     .attr('class', 'modal-section commit-changes-section')
92626                     .merge(changesSection);
92627
92628                 // Change summary
92629                 changesSection.call(commitChanges.render);
92630
92631
92632                 function toggleRequestReview() {
92633                     var rr = requestReviewInput.property('checked');
92634                     updateChangeset({ review_requested: (rr ? 'yes' : undefined) });
92635
92636                     tagSection
92637                         .call(rawTagEditor
92638                             .tags(Object.assign({}, context.changeset.tags))   // shallow copy
92639                             .render
92640                         );
92641                 }
92642             }
92643
92644
92645             function getUploadBlockerMessage() {
92646                 var errors = context.validator()
92647                     .getIssuesBySeverity({ what: 'edited', where: 'all' }).error;
92648
92649                 if (errors.length) {
92650                     return _t('commit.outstanding_errors_message', { count: errors.length });
92651
92652                 } else {
92653                     var hasChangesetComment = context.changeset && context.changeset.tags.comment && context.changeset.tags.comment.trim().length;
92654                     if (!hasChangesetComment) {
92655                         return _t('commit.comment_needed_message');
92656                     }
92657                 }
92658                 return null;
92659             }
92660
92661
92662             function changeTags(_, changed, onInput) {
92663                 if (changed.hasOwnProperty('comment')) {
92664                     if (changed.comment === undefined) {
92665                         changed.comment = '';
92666                     }
92667                     if (!onInput) {
92668                         corePreferences('comment', changed.comment);
92669                         corePreferences('commentDate', Date.now());
92670                     }
92671                 }
92672                 if (changed.hasOwnProperty('source')) {
92673                     if (changed.source === undefined) {
92674                         corePreferences('source', null);
92675                     } else if (!onInput) {
92676                         corePreferences('source', changed.source);
92677                         corePreferences('commentDate', Date.now());
92678                     }
92679                 }
92680                 // no need to update `prefs` for `hashtags` here since it's done in `updateChangeset`
92681
92682                 updateChangeset(changed, onInput);
92683
92684                 if (_selection) {
92685                     _selection.call(render);
92686                 }
92687             }
92688
92689
92690             function findHashtags(tags, commentOnly) {
92691                 var detectedHashtags = commentHashtags();
92692
92693                 if (detectedHashtags.length) {
92694                     // always remove stored hashtags if there are hashtags in the comment - #4304
92695                     corePreferences('hashtags', null);
92696                 }
92697                 if (!detectedHashtags.length || !commentOnly) {
92698                     detectedHashtags = detectedHashtags.concat(hashtagHashtags());
92699                 }
92700
92701                 var allLowerCase = new Set();
92702                 return detectedHashtags.filter(function(hashtag) {
92703                     // Compare tags as lowercase strings, but keep original case tags
92704                     var lowerCase = hashtag.toLowerCase();
92705                     if (!allLowerCase.has(lowerCase)) {
92706                         allLowerCase.add(lowerCase);
92707                         return true;
92708                     }
92709                     return false;
92710                 });
92711
92712                 // Extract hashtags from `comment`
92713                 function commentHashtags() {
92714                     var matches = (tags.comment || '')
92715                         .replace(/http\S*/g, '')  // drop anything that looks like a URL - #4289
92716                         .match(hashtagRegex);
92717
92718                     return matches || [];
92719                 }
92720
92721                 // Extract and clean hashtags from `hashtags`
92722                 function hashtagHashtags() {
92723                     var matches = (tags.hashtags || '')
92724                         .split(/[,;\s]+/)
92725                         .map(function (s) {
92726                             if (s[0] !== '#') { s = '#' + s; }    // prepend '#'
92727                             var matched = s.match(hashtagRegex);
92728                             return matched && matched[0];
92729                         }).filter(Boolean);                       // exclude falsy
92730
92731                     return matches || [];
92732                 }
92733             }
92734
92735
92736             function isReviewRequested(tags) {
92737                 var rr = tags.review_requested;
92738                 if (rr === undefined) return false;
92739                 rr = rr.trim().toLowerCase();
92740                 return !(rr === '' || rr === 'no');
92741             }
92742
92743
92744             function updateChangeset(changed, onInput) {
92745                 var tags = Object.assign({}, context.changeset.tags);   // shallow copy
92746
92747                 Object.keys(changed).forEach(function(k) {
92748                     var v = changed[k];
92749                     k = context.cleanTagKey(k);
92750                     if (readOnlyTags.indexOf(k) !== -1) return;
92751
92752                     if (k !== '' && v !== undefined) {
92753                         if (onInput) {
92754                             tags[k] = v;
92755                         } else {
92756                             tags[k] = context.cleanTagValue(v);
92757                         }
92758                     } else {
92759                         delete tags[k];
92760                     }
92761                 });
92762
92763                 if (!onInput) {
92764                     // when changing the comment, override hashtags with any found in comment.
92765                     var commentOnly = changed.hasOwnProperty('comment') && (changed.comment !== '');
92766                     var arr = findHashtags(tags, commentOnly);
92767                     if (arr.length) {
92768                         tags.hashtags = context.cleanTagValue(arr.join(';'));
92769                         corePreferences('hashtags', tags.hashtags);
92770                     } else {
92771                         delete tags.hashtags;
92772                         corePreferences('hashtags', null);
92773                     }
92774                 }
92775
92776                 // always update userdetails, just in case user reauthenticates as someone else
92777                 if (_userDetails && _userDetails.changesets_count !== undefined) {
92778                     var changesetsCount = parseInt(_userDetails.changesets_count, 10) + 1;  // #4283
92779                     tags.changesets_count = String(changesetsCount);
92780
92781                     // first 100 edits - new user
92782                     if (changesetsCount <= 100) {
92783                         var s;
92784                         s = corePreferences('walkthrough_completed');
92785                         if (s) {
92786                             tags['ideditor:walkthrough_completed'] = s;
92787                         }
92788
92789                         s = corePreferences('walkthrough_progress');
92790                         if (s) {
92791                             tags['ideditor:walkthrough_progress'] = s;
92792                         }
92793
92794                         s = corePreferences('walkthrough_started');
92795                         if (s) {
92796                             tags['ideditor:walkthrough_started'] = s;
92797                         }
92798                     }
92799                 } else {
92800                     delete tags.changesets_count;
92801                 }
92802
92803                 if (!fastDeepEqual(context.changeset.tags, tags)) {
92804                     context.changeset = context.changeset.update({ tags: tags });
92805                 }
92806             }
92807
92808
92809             commit.reset = function() {
92810                 context.changeset = null;
92811             };
92812
92813
92814             return utilRebind(commit, dispatch$1, 'on');
92815         }
92816
92817         var RADIUS = 6378137;
92818         var FLATTENING = 1/298.257223563;
92819         var POLAR_RADIUS$1 = 6356752.3142;
92820
92821         var wgs84 = {
92822                 RADIUS: RADIUS,
92823                 FLATTENING: FLATTENING,
92824                 POLAR_RADIUS: POLAR_RADIUS$1
92825         };
92826
92827         var geometry_1 = geometry;
92828         var ring = ringArea;
92829
92830         function geometry(_) {
92831             var area = 0, i;
92832             switch (_.type) {
92833                 case 'Polygon':
92834                     return polygonArea(_.coordinates);
92835                 case 'MultiPolygon':
92836                     for (i = 0; i < _.coordinates.length; i++) {
92837                         area += polygonArea(_.coordinates[i]);
92838                     }
92839                     return area;
92840                 case 'Point':
92841                 case 'MultiPoint':
92842                 case 'LineString':
92843                 case 'MultiLineString':
92844                     return 0;
92845                 case 'GeometryCollection':
92846                     for (i = 0; i < _.geometries.length; i++) {
92847                         area += geometry(_.geometries[i]);
92848                     }
92849                     return area;
92850             }
92851         }
92852
92853         function polygonArea(coords) {
92854             var area = 0;
92855             if (coords && coords.length > 0) {
92856                 area += Math.abs(ringArea(coords[0]));
92857                 for (var i = 1; i < coords.length; i++) {
92858                     area -= Math.abs(ringArea(coords[i]));
92859                 }
92860             }
92861             return area;
92862         }
92863
92864         /**
92865          * Calculate the approximate area of the polygon were it projected onto
92866          *     the earth.  Note that this area will be positive if ring is oriented
92867          *     clockwise, otherwise it will be negative.
92868          *
92869          * Reference:
92870          * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
92871          *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
92872          *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
92873          *
92874          * Returns:
92875          * {float} The approximate signed geodesic area of the polygon in square
92876          *     meters.
92877          */
92878
92879         function ringArea(coords) {
92880             var p1, p2, p3, lowerIndex, middleIndex, upperIndex, i,
92881             area = 0,
92882             coordsLength = coords.length;
92883
92884             if (coordsLength > 2) {
92885                 for (i = 0; i < coordsLength; i++) {
92886                     if (i === coordsLength - 2) {// i = N-2
92887                         lowerIndex = coordsLength - 2;
92888                         middleIndex = coordsLength -1;
92889                         upperIndex = 0;
92890                     } else if (i === coordsLength - 1) {// i = N-1
92891                         lowerIndex = coordsLength - 1;
92892                         middleIndex = 0;
92893                         upperIndex = 1;
92894                     } else { // i = 0 to N-3
92895                         lowerIndex = i;
92896                         middleIndex = i+1;
92897                         upperIndex = i+2;
92898                     }
92899                     p1 = coords[lowerIndex];
92900                     p2 = coords[middleIndex];
92901                     p3 = coords[upperIndex];
92902                     area += ( rad(p3[0]) - rad(p1[0]) ) * Math.sin( rad(p2[1]));
92903                 }
92904
92905                 area = area * wgs84.RADIUS * wgs84.RADIUS / 2;
92906             }
92907
92908             return area;
92909         }
92910
92911         function rad(_) {
92912             return _ * Math.PI / 180;
92913         }
92914
92915         var geojsonArea = {
92916                 geometry: geometry_1,
92917                 ring: ring
92918         };
92919
92920         function toRadians(angleInDegrees) {
92921           return (angleInDegrees * Math.PI) / 180;
92922         }
92923
92924         function toDegrees(angleInRadians) {
92925           return (angleInRadians * 180) / Math.PI;
92926         }
92927
92928         function offset(c1, distance, bearing) {
92929           var lat1 = toRadians(c1[1]);
92930           var lon1 = toRadians(c1[0]);
92931           var dByR = distance / 6378137; // distance divided by 6378137 (radius of the earth) wgs84
92932           var lat = Math.asin(
92933             Math.sin(lat1) * Math.cos(dByR) +
92934               Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing)
92935           );
92936           var lon =
92937             lon1 +
92938             Math.atan2(
92939               Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1),
92940               Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat)
92941             );
92942           return [toDegrees(lon), toDegrees(lat)];
92943         }
92944
92945         function validateCenter(center) {
92946           const validCenterLengths = [2, 3];
92947           if (!Array.isArray(center) || !validCenterLengths.includes(center.length)) {
92948             throw new Error("ERROR! Center has to be an array of length two or three");
92949           }
92950           const [lng, lat] = center;
92951           if (typeof lng !== "number" || typeof lat !== "number") {
92952             throw new Error(
92953               `ERROR! Longitude and Latitude has to be numbers but where ${typeof lng} and ${typeof lat}`
92954             );
92955           }
92956           if (lng > 180 || lng < -180) {
92957             throw new Error(
92958               `ERROR! Longitude has to be between -180 and 180 but was ${lng}`
92959             );
92960           }
92961
92962           if (lat > 90 || lat < -90) {
92963             throw new Error(
92964               `ERROR! Latitude has to be between -90 and 90 but was ${lat}`
92965             );
92966           }
92967         }
92968
92969         function validateRadius(radius) {
92970           if (typeof radius !== "number") {
92971             throw new Error(
92972               `ERROR! Radius has to be a positive number but was: ${typeof radius}`
92973             );
92974           }
92975
92976           if (radius <= 0) {
92977             throw new Error(
92978               `ERROR! Radius has to be a positive number but was: ${radius}`
92979             );
92980           }
92981         }
92982
92983         function validateNumberOfSegments(numberOfSegments) {
92984           if (typeof numberOfSegments !== "number" && numberOfSegments !== undefined) {
92985             throw new Error(
92986               `ERROR! Number of segments has to be a number but was: ${typeof numberOfSegments}`
92987             );
92988           }
92989
92990           if (numberOfSegments < 3) {
92991             throw new Error(
92992               `ERROR! Number of segments has to be at least 3 but was: ${numberOfSegments}`
92993             );
92994           }
92995         }
92996
92997         function validateInput({ center, radius, numberOfSegments }) {
92998           validateCenter(center);
92999           validateRadius(radius);
93000           validateNumberOfSegments(numberOfSegments);
93001         }
93002
93003         var circleToPolygon = function circleToPolygon(center, radius, numberOfSegments) {
93004           var n = numberOfSegments ? numberOfSegments : 32;
93005
93006           // validateInput() throws error on invalid input and do nothing on valid input
93007           validateInput({ center, radius, numberOfSegments });
93008
93009           var coordinates = [];
93010           for (var i = 0; i < n; ++i) {
93011             coordinates.push(offset(center, radius, (2 * Math.PI * -i) / n));
93012           }
93013           coordinates.push(coordinates[0]);
93014
93015           return {
93016             type: "Polygon",
93017             coordinates: [coordinates]
93018           };
93019         };
93020
93021         var geojsonPrecision = createCommonjsModule(function (module) {
93022         (function() {
93023
93024           function parse(t, coordinatePrecision, extrasPrecision) {
93025
93026             function point(p) {
93027               return p.map(function(e, index) {
93028                 if (index < 2) {
93029                     return 1 * e.toFixed(coordinatePrecision);
93030                 } else {
93031                     return 1 * e.toFixed(extrasPrecision);
93032                 }
93033               });
93034             }
93035
93036             function multi(l) {
93037               return l.map(point);
93038             }
93039
93040             function poly(p) {
93041               return p.map(multi);
93042             }
93043
93044             function multiPoly(m) {
93045               return m.map(poly);
93046             }
93047
93048             function geometry(obj) {
93049               if (!obj) {
93050                 return {};
93051               }
93052               
93053               switch (obj.type) {
93054                 case "Point":
93055                   obj.coordinates = point(obj.coordinates);
93056                   return obj;
93057                 case "LineString":
93058                 case "MultiPoint":
93059                   obj.coordinates = multi(obj.coordinates);
93060                   return obj;
93061                 case "Polygon":
93062                 case "MultiLineString":
93063                   obj.coordinates = poly(obj.coordinates);
93064                   return obj;
93065                 case "MultiPolygon":
93066                   obj.coordinates = multiPoly(obj.coordinates);
93067                   return obj;
93068                 case "GeometryCollection":
93069                   obj.geometries = obj.geometries.map(geometry);
93070                   return obj;
93071                 default :
93072                   return {};
93073               }
93074             }
93075
93076             function feature(obj) {
93077               obj.geometry = geometry(obj.geometry);
93078               return obj
93079             }
93080
93081             function featureCollection(f) {
93082               f.features = f.features.map(feature);
93083               return f;
93084             }
93085
93086             function geometryCollection(g) {
93087               g.geometries = g.geometries.map(geometry);
93088               return g;
93089             }
93090
93091             if (!t) {
93092               return t;
93093             }
93094
93095             switch (t.type) {
93096               case "Feature":
93097                 return feature(t);
93098               case "GeometryCollection" :
93099                 return geometryCollection(t);
93100               case "FeatureCollection" :
93101                 return featureCollection(t);
93102               case "Point":
93103               case "LineString":
93104               case "Polygon":
93105               case "MultiPoint":
93106               case "MultiPolygon":
93107               case "MultiLineString":
93108                 return geometry(t);
93109               default :
93110                 return t;
93111             }
93112               
93113           }
93114
93115           module.exports = parse;
93116           module.exports.parse = parse;
93117
93118         }());
93119         });
93120
93121         /* Polyfill service v3.13.0
93122          * For detailed credits and licence information see http://github.com/financial-times/polyfill-service
93123          *
93124          * - Array.prototype.fill, License: CC0 */
93125
93126         if (!('fill' in Array.prototype)) {
93127           Object.defineProperty(Array.prototype, 'fill', {
93128             configurable: true,
93129             value: function fill (value) {
93130               if (this === undefined || this === null) {
93131                 throw new TypeError(this + ' is not an object')
93132               }
93133
93134               var arrayLike = Object(this);
93135
93136               var length = Math.max(Math.min(arrayLike.length, 9007199254740991), 0) || 0;
93137
93138               var relativeStart = 1 in arguments ? parseInt(Number(arguments[1]), 10) || 0 : 0;
93139
93140               relativeStart = relativeStart < 0 ? Math.max(length + relativeStart, 0) : Math.min(relativeStart, length);
93141
93142               var relativeEnd = 2 in arguments && arguments[2] !== undefined ? parseInt(Number(arguments[2]), 10) || 0 : length;
93143
93144               relativeEnd = relativeEnd < 0 ? Math.max(length + arguments[2], 0) : Math.min(relativeEnd, length);
93145
93146               while (relativeStart < relativeEnd) {
93147                 arrayLike[relativeStart] = value;
93148
93149                 ++relativeStart;
93150               }
93151
93152               return arrayLike
93153             },
93154             writable: true
93155           });
93156         }
93157
93158         /**
93159          * Polyfill for IE support
93160          */
93161         Number.isFinite = Number.isFinite || function (value) {
93162           return typeof value === 'number' && isFinite(value)
93163         };
93164
93165         Number.isInteger = Number.isInteger || function (val) {
93166           return typeof val === 'number' &&
93167           isFinite(val) &&
93168           Math.floor(val) === val
93169         };
93170
93171         Number.parseFloat = Number.parseFloat || parseFloat;
93172
93173         Number.isNaN = Number.isNaN || function (value) {
93174           return value !== value // eslint-disable-line
93175         };
93176
93177         /**
93178          * Polyfill for IE support
93179          */
93180         Math.trunc = Math.trunc || function (x) {
93181           return x < 0 ? Math.ceil(x) : Math.floor(x)
93182         };
93183
93184         var NumberUtil = function NumberUtil () {};
93185
93186         NumberUtil.prototype.interfaces_ = function interfaces_ () {
93187           return []
93188         };
93189         NumberUtil.prototype.getClass = function getClass () {
93190           return NumberUtil
93191         };
93192         NumberUtil.prototype.equalsWithTolerance = function equalsWithTolerance (x1, x2, tolerance) {
93193           return Math.abs(x1 - x2) <= tolerance
93194         };
93195
93196         var IllegalArgumentException = (function (Error) {
93197                 function IllegalArgumentException (message) {
93198                         Error.call(this, message);
93199                         this.name = 'IllegalArgumentException';
93200                         this.message = message;
93201                         this.stack = (new Error()).stack;
93202                 }
93203
93204                 if ( Error ) IllegalArgumentException.__proto__ = Error;
93205                 IllegalArgumentException.prototype = Object.create( Error && Error.prototype );
93206                 IllegalArgumentException.prototype.constructor = IllegalArgumentException;
93207
93208                 return IllegalArgumentException;
93209         }(Error));
93210
93211         var Double = function Double () {};
93212
93213         var staticAccessors$1 = { MAX_VALUE: { configurable: true } };
93214
93215         Double.isNaN = function isNaN (n) { return Number.isNaN(n) };
93216         Double.doubleToLongBits = function doubleToLongBits (n) { return n };
93217         Double.longBitsToDouble = function longBitsToDouble (n) { return n };
93218         Double.isInfinite = function isInfinite (n) { return !Number.isFinite(n) };
93219         staticAccessors$1.MAX_VALUE.get = function () { return Number.MAX_VALUE };
93220
93221         Object.defineProperties( Double, staticAccessors$1 );
93222
93223         var Comparable = function Comparable () {};
93224
93225         var Clonable = function Clonable () {};
93226
93227         var Comparator = function Comparator () {};
93228
93229         function Serializable () {}
93230
93231         // import Assert from '../util/Assert'
93232
93233         var Coordinate = function Coordinate () {
93234           this.x = null;
93235           this.y = null;
93236           this.z = null;
93237           if (arguments.length === 0) {
93238             this.x = 0.0;
93239             this.y = 0.0;
93240             this.z = Coordinate.NULL_ORDINATE;
93241           } else if (arguments.length === 1) {
93242             var c = arguments[0];
93243             this.x = c.x;
93244             this.y = c.y;
93245             this.z = c.z;
93246           } else if (arguments.length === 2) {
93247             this.x = arguments[0];
93248             this.y = arguments[1];
93249             this.z = Coordinate.NULL_ORDINATE;
93250           } else if (arguments.length === 3) {
93251             this.x = arguments[0];
93252             this.y = arguments[1];
93253             this.z = arguments[2];
93254           }
93255         };
93256
93257         var staticAccessors = { DimensionalComparator: { configurable: true },serialVersionUID: { configurable: true },NULL_ORDINATE: { configurable: true },X: { configurable: true },Y: { configurable: true },Z: { configurable: true } };
93258         Coordinate.prototype.setOrdinate = function setOrdinate (ordinateIndex, value) {
93259           switch (ordinateIndex) {
93260             case Coordinate.X:
93261               this.x = value;
93262               break
93263             case Coordinate.Y:
93264               this.y = value;
93265               break
93266             case Coordinate.Z:
93267               this.z = value;
93268               break
93269             default:
93270               throw new IllegalArgumentException('Invalid ordinate index: ' + ordinateIndex)
93271           }
93272         };
93273         Coordinate.prototype.equals2D = function equals2D () {
93274           if (arguments.length === 1) {
93275             var other = arguments[0];
93276             if (this.x !== other.x) {
93277               return false
93278             }
93279             if (this.y !== other.y) {
93280               return false
93281             }
93282             return true
93283           } else if (arguments.length === 2) {
93284             var c = arguments[0];
93285             var tolerance = arguments[1];
93286             if (!NumberUtil.equalsWithTolerance(this.x, c.x, tolerance)) {
93287               return false
93288             }
93289             if (!NumberUtil.equalsWithTolerance(this.y, c.y, tolerance)) {
93290               return false
93291             }
93292             return true
93293           }
93294         };
93295         Coordinate.prototype.getOrdinate = function getOrdinate (ordinateIndex) {
93296           switch (ordinateIndex) {
93297             case Coordinate.X:
93298               return this.x
93299             case Coordinate.Y:
93300               return this.y
93301             case Coordinate.Z:
93302               return this.z
93303           }
93304           throw new IllegalArgumentException('Invalid ordinate index: ' + ordinateIndex)
93305         };
93306         Coordinate.prototype.equals3D = function equals3D (other) {
93307           return this.x === other.x &&
93308                  this.y === other.y &&
93309                  ((this.z === other.z || Double.isNaN(this.z)) &&
93310                  Double.isNaN(other.z))
93311         };
93312         Coordinate.prototype.equals = function equals (other) {
93313           if (!(other instanceof Coordinate)) {
93314             return false
93315           }
93316           return this.equals2D(other)
93317         };
93318         Coordinate.prototype.equalInZ = function equalInZ (c, tolerance) {
93319           return NumberUtil.equalsWithTolerance(this.z, c.z, tolerance)
93320         };
93321         Coordinate.prototype.compareTo = function compareTo (o) {
93322           var other = o;
93323           if (this.x < other.x) { return -1 }
93324           if (this.x > other.x) { return 1 }
93325           if (this.y < other.y) { return -1 }
93326           if (this.y > other.y) { return 1 }
93327           return 0
93328         };
93329         Coordinate.prototype.clone = function clone () {
93330           // try {
93331           // var coord = null
93332           // return coord
93333           // } catch (e) {
93334           // if (e instanceof CloneNotSupportedException) {
93335           //   Assert.shouldNeverReachHere("this shouldn't happen because this class is Cloneable")
93336           //   return null
93337           // } else throw e
93338           // } finally {}
93339         };
93340         Coordinate.prototype.copy = function copy () {
93341           return new Coordinate(this)
93342         };
93343         Coordinate.prototype.toString = function toString () {
93344           return '(' + this.x + ', ' + this.y + ', ' + this.z + ')'
93345         };
93346         Coordinate.prototype.distance3D = function distance3D (c) {
93347           var dx = this.x - c.x;
93348           var dy = this.y - c.y;
93349           var dz = this.z - c.z;
93350           return Math.sqrt(dx * dx + dy * dy + dz * dz)
93351         };
93352         Coordinate.prototype.distance = function distance (c) {
93353           var dx = this.x - c.x;
93354           var dy = this.y - c.y;
93355           return Math.sqrt(dx * dx + dy * dy)
93356         };
93357         Coordinate.prototype.hashCode = function hashCode () {
93358           var result = 17;
93359           result = 37 * result + Coordinate.hashCode(this.x);
93360           result = 37 * result + Coordinate.hashCode(this.y);
93361           return result
93362         };
93363         Coordinate.prototype.setCoordinate = function setCoordinate (other) {
93364           this.x = other.x;
93365           this.y = other.y;
93366           this.z = other.z;
93367         };
93368         Coordinate.prototype.interfaces_ = function interfaces_ () {
93369           return [Comparable, Clonable, Serializable]
93370         };
93371         Coordinate.prototype.getClass = function getClass () {
93372           return Coordinate
93373         };
93374         Coordinate.hashCode = function hashCode () {
93375           if (arguments.length === 1) {
93376             var x = arguments[0];
93377             var f = Double.doubleToLongBits(x);
93378             return Math.trunc((f ^ f) >>> 32)
93379           }
93380         };
93381         staticAccessors.DimensionalComparator.get = function () { return DimensionalComparator };
93382         staticAccessors.serialVersionUID.get = function () { return 6683108902428366910 };
93383         staticAccessors.NULL_ORDINATE.get = function () { return Double.NaN };
93384         staticAccessors.X.get = function () { return 0 };
93385         staticAccessors.Y.get = function () { return 1 };
93386         staticAccessors.Z.get = function () { return 2 };
93387
93388         Object.defineProperties( Coordinate, staticAccessors );
93389
93390         var DimensionalComparator = function DimensionalComparator (dimensionsToTest) {
93391           this._dimensionsToTest = 2;
93392           if (arguments.length === 0) ; else if (arguments.length === 1) {
93393             var dimensionsToTest$1 = arguments[0];
93394             if (dimensionsToTest$1 !== 2 && dimensionsToTest$1 !== 3) { throw new IllegalArgumentException('only 2 or 3 dimensions may be specified') }
93395             this._dimensionsToTest = dimensionsToTest$1;
93396           }
93397         };
93398         DimensionalComparator.prototype.compare = function compare (o1, o2) {
93399           var c1 = o1;
93400           var c2 = o2;
93401           var compX = DimensionalComparator.compare(c1.x, c2.x);
93402           if (compX !== 0) { return compX }
93403           var compY = DimensionalComparator.compare(c1.y, c2.y);
93404           if (compY !== 0) { return compY }
93405           if (this._dimensionsToTest <= 2) { return 0 }
93406           var compZ = DimensionalComparator.compare(c1.z, c2.z);
93407           return compZ
93408         };
93409         DimensionalComparator.prototype.interfaces_ = function interfaces_ () {
93410           return [Comparator]
93411         };
93412         DimensionalComparator.prototype.getClass = function getClass () {
93413           return DimensionalComparator
93414         };
93415         DimensionalComparator.compare = function compare (a, b) {
93416           if (a < b) { return -1 }
93417           if (a > b) { return 1 }
93418           if (Double.isNaN(a)) {
93419             if (Double.isNaN(b)) { return 0 }
93420             return -1
93421           }
93422           if (Double.isNaN(b)) { return 1 }
93423           return 0
93424         };
93425
93426         // import hasInterface from '../../../../hasInterface'
93427         // import CoordinateSequence from './CoordinateSequence'
93428
93429         var CoordinateSequenceFactory = function CoordinateSequenceFactory () {};
93430
93431         CoordinateSequenceFactory.prototype.create = function create () {
93432           // if (arguments.length === 1) {
93433           // if (arguments[0] instanceof Array) {
93434           //   let coordinates = arguments[0]
93435           // } else if (hasInterface(arguments[0], CoordinateSequence)) {
93436           //   let coordSeq = arguments[0]
93437           // }
93438           // } else if (arguments.length === 2) {
93439           // let size = arguments[0]
93440           // let dimension = arguments[1]
93441           // }
93442         };
93443         CoordinateSequenceFactory.prototype.interfaces_ = function interfaces_ () {
93444           return []
93445         };
93446         CoordinateSequenceFactory.prototype.getClass = function getClass () {
93447           return CoordinateSequenceFactory
93448         };
93449
93450         var Location = function Location () {};
93451
93452         var staticAccessors$4 = { INTERIOR: { configurable: true },BOUNDARY: { configurable: true },EXTERIOR: { configurable: true },NONE: { configurable: true } };
93453
93454         Location.prototype.interfaces_ = function interfaces_ () {
93455           return []
93456         };
93457         Location.prototype.getClass = function getClass () {
93458           return Location
93459         };
93460         Location.toLocationSymbol = function toLocationSymbol (locationValue) {
93461           switch (locationValue) {
93462             case Location.EXTERIOR:
93463               return 'e'
93464             case Location.BOUNDARY:
93465               return 'b'
93466             case Location.INTERIOR:
93467               return 'i'
93468             case Location.NONE:
93469               return '-'
93470           }
93471           throw new IllegalArgumentException('Unknown location value: ' + locationValue)
93472         };
93473         staticAccessors$4.INTERIOR.get = function () { return 0 };
93474         staticAccessors$4.BOUNDARY.get = function () { return 1 };
93475         staticAccessors$4.EXTERIOR.get = function () { return 2 };
93476         staticAccessors$4.NONE.get = function () { return -1 };
93477
93478         Object.defineProperties( Location, staticAccessors$4 );
93479
93480         var hasInterface = function (o, i) {
93481           return o.interfaces_ && o.interfaces_().indexOf(i) > -1
93482         };
93483
93484         var MathUtil = function MathUtil () {};
93485
93486         var staticAccessors$5 = { LOG_10: { configurable: true } };
93487
93488         MathUtil.prototype.interfaces_ = function interfaces_ () {
93489           return []
93490         };
93491         MathUtil.prototype.getClass = function getClass () {
93492           return MathUtil
93493         };
93494         MathUtil.log10 = function log10 (x) {
93495           var ln = Math.log(x);
93496           if (Double.isInfinite(ln)) { return ln }
93497           if (Double.isNaN(ln)) { return ln }
93498           return ln / MathUtil.LOG_10
93499         };
93500         MathUtil.min = function min (v1, v2, v3, v4) {
93501           var min = v1;
93502           if (v2 < min) { min = v2; }
93503           if (v3 < min) { min = v3; }
93504           if (v4 < min) { min = v4; }
93505           return min
93506         };
93507         MathUtil.clamp = function clamp () {
93508           if (typeof arguments[2] === 'number' && (typeof arguments[0] === 'number' && typeof arguments[1] === 'number')) {
93509             var x = arguments[0];
93510             var min = arguments[1];
93511             var max = arguments[2];
93512             if (x < min) { return min }
93513             if (x > max) { return max }
93514             return x
93515           } else if (Number.isInteger(arguments[2]) && (Number.isInteger(arguments[0]) && Number.isInteger(arguments[1]))) {
93516             var x$1 = arguments[0];
93517             var min$1 = arguments[1];
93518             var max$1 = arguments[2];
93519             if (x$1 < min$1) { return min$1 }
93520             if (x$1 > max$1) { return max$1 }
93521             return x$1
93522           }
93523         };
93524         MathUtil.wrap = function wrap (index, max) {
93525           if (index < 0) {
93526             return max - -index % max
93527           }
93528           return index % max
93529         };
93530         MathUtil.max = function max () {
93531           if (arguments.length === 3) {
93532             var v1 = arguments[0];
93533             var v2 = arguments[1];
93534             var v3 = arguments[2];
93535             var max = v1;
93536             if (v2 > max) { max = v2; }
93537             if (v3 > max) { max = v3; }
93538             return max
93539           } else if (arguments.length === 4) {
93540             var v1$1 = arguments[0];
93541             var v2$1 = arguments[1];
93542             var v3$1 = arguments[2];
93543             var v4 = arguments[3];
93544             var max$1 = v1$1;
93545             if (v2$1 > max$1) { max$1 = v2$1; }
93546             if (v3$1 > max$1) { max$1 = v3$1; }
93547             if (v4 > max$1) { max$1 = v4; }
93548             return max$1
93549           }
93550         };
93551         MathUtil.average = function average (x1, x2) {
93552           return (x1 + x2) / 2.0
93553         };
93554         staticAccessors$5.LOG_10.get = function () { return Math.log(10) };
93555
93556         Object.defineProperties( MathUtil, staticAccessors$5 );
93557
93558         var StringBuffer = function StringBuffer (str) {
93559           this.str = str;
93560         };
93561         StringBuffer.prototype.append = function append (e) {
93562           this.str += e;
93563         };
93564
93565         StringBuffer.prototype.setCharAt = function setCharAt (i, c) {
93566           this.str = this.str.substr(0, i) + c + this.str.substr(i + 1);
93567         };
93568
93569         StringBuffer.prototype.toString = function toString (e) {
93570           return this.str
93571         };
93572
93573         var Integer = function Integer (value) {
93574           this.value = value;
93575         };
93576         Integer.prototype.intValue = function intValue () {
93577           return this.value
93578         };
93579         Integer.prototype.compareTo = function compareTo (o) {
93580           if (this.value < o) { return -1 }
93581           if (this.value > o) { return 1 }
93582           return 0
93583         };
93584         Integer.isNaN = function isNaN (n) { return Number.isNaN(n) };
93585
93586         var Character = function Character () {};
93587
93588         Character.isWhitespace = function isWhitespace (c) { return ((c <= 32 && c >= 0) || c === 127) };
93589         Character.toUpperCase = function toUpperCase (c) { return c.toUpperCase() };
93590
93591         var DD = function DD () {
93592           this._hi = 0.0;
93593           this._lo = 0.0;
93594           if (arguments.length === 0) {
93595             this.init(0.0);
93596           } else if (arguments.length === 1) {
93597             if (typeof arguments[0] === 'number') {
93598               var x = arguments[0];
93599               this.init(x);
93600             } else if (arguments[0] instanceof DD) {
93601               var dd = arguments[0];
93602               this.init(dd);
93603             } else if (typeof arguments[0] === 'string') {
93604               var str = arguments[0];
93605               DD.call(this, DD.parse(str));
93606             }
93607           } else if (arguments.length === 2) {
93608             var hi = arguments[0];
93609             var lo = arguments[1];
93610             this.init(hi, lo);
93611           }
93612         };
93613
93614         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 } };
93615         DD.prototype.le = function le (y) {
93616           return (this._hi < y._hi || this._hi === y._hi) && this._lo <= y._lo
93617         };
93618         DD.prototype.extractSignificantDigits = function extractSignificantDigits (insertDecimalPoint, magnitude) {
93619           var y = this.abs();
93620           var mag = DD.magnitude(y._hi);
93621           var scale = DD.TEN.pow(mag);
93622           y = y.divide(scale);
93623           if (y.gt(DD.TEN)) {
93624             y = y.divide(DD.TEN);
93625             mag += 1;
93626           } else if (y.lt(DD.ONE)) {
93627             y = y.multiply(DD.TEN);
93628             mag -= 1;
93629           }
93630           var decimalPointPos = mag + 1;
93631           var buf = new StringBuffer();
93632           var numDigits = DD.MAX_PRINT_DIGITS - 1;
93633           for (var i = 0; i <= numDigits; i++) {
93634             if (insertDecimalPoint && i === decimalPointPos) {
93635               buf.append('.');
93636             }
93637             var digit = Math.trunc(y._hi);
93638             if (digit < 0) {
93639               break
93640             }
93641             var rebiasBy10 = false;
93642             var digitChar = 0;
93643             if (digit > 9) {
93644               rebiasBy10 = true;
93645               digitChar = '9';
93646             } else {
93647               digitChar = '0' + digit;
93648             }
93649             buf.append(digitChar);
93650             y = y.subtract(DD.valueOf(digit)).multiply(DD.TEN);
93651             if (rebiasBy10) { y.selfAdd(DD.TEN); }
93652             var continueExtractingDigits = true;
93653             var remMag = DD.magnitude(y._hi);
93654             if (remMag < 0 && Math.abs(remMag) >= numDigits - i) { continueExtractingDigits = false; }
93655             if (!continueExtractingDigits) { break }
93656           }
93657           magnitude[0] = mag;
93658           return buf.toString()
93659         };
93660         DD.prototype.sqr = function sqr () {
93661           return this.multiply(this)
93662         };
93663         DD.prototype.doubleValue = function doubleValue () {
93664           return this._hi + this._lo
93665         };
93666         DD.prototype.subtract = function subtract () {
93667           if (arguments[0] instanceof DD) {
93668             var y = arguments[0];
93669             return this.add(y.negate())
93670           } else if (typeof arguments[0] === 'number') {
93671             var y$1 = arguments[0];
93672             return this.add(-y$1)
93673           }
93674         };
93675         DD.prototype.equals = function equals () {
93676           if (arguments.length === 1) {
93677             var y = arguments[0];
93678             return this._hi === y._hi && this._lo === y._lo
93679           }
93680         };
93681         DD.prototype.isZero = function isZero () {
93682           return this._hi === 0.0 && this._lo === 0.0
93683         };
93684         DD.prototype.selfSubtract = function selfSubtract () {
93685           if (arguments[0] instanceof DD) {
93686             var y = arguments[0];
93687             if (this.isNaN()) { return this }
93688             return this.selfAdd(-y._hi, -y._lo)
93689           } else if (typeof arguments[0] === 'number') {
93690             var y$1 = arguments[0];
93691             if (this.isNaN()) { return this }
93692             return this.selfAdd(-y$1, 0.0)
93693           }
93694         };
93695         DD.prototype.getSpecialNumberString = function getSpecialNumberString () {
93696           if (this.isZero()) { return '0.0' }
93697           if (this.isNaN()) { return 'NaN ' }
93698           return null
93699         };
93700         DD.prototype.min = function min (x) {
93701           if (this.le(x)) {
93702             return this
93703           } else {
93704             return x
93705           }
93706         };
93707         DD.prototype.selfDivide = function selfDivide () {
93708           if (arguments.length === 1) {
93709             if (arguments[0] instanceof DD) {
93710               var y = arguments[0];
93711               return this.selfDivide(y._hi, y._lo)
93712             } else if (typeof arguments[0] === 'number') {
93713               var y$1 = arguments[0];
93714               return this.selfDivide(y$1, 0.0)
93715             }
93716           } else if (arguments.length === 2) {
93717             var yhi = arguments[0];
93718             var ylo = arguments[1];
93719             var hc = null;
93720             var tc = null;
93721             var hy = null;
93722             var ty = null;
93723             var C = null;
93724             var c = null;
93725             var U = null;
93726             var u = null;
93727             C = this._hi / yhi;
93728             c = DD.SPLIT * C;
93729             hc = c - C;
93730             u = DD.SPLIT * yhi;
93731             hc = c - hc;
93732             tc = C - hc;
93733             hy = u - yhi;
93734             U = C * yhi;
93735             hy = u - hy;
93736             ty = yhi - hy;
93737             u = hc * hy - U + hc * ty + tc * hy + tc * ty;
93738             c = (this._hi - U - u + this._lo - C * ylo) / yhi;
93739             u = C + c;
93740             this._hi = u;
93741             this._lo = C - u + c;
93742             return this
93743           }
93744         };
93745         DD.prototype.dump = function dump () {
93746           return 'DD<' + this._hi + ', ' + this._lo + '>'
93747         };
93748         DD.prototype.divide = function divide () {
93749           if (arguments[0] instanceof DD) {
93750             var y = arguments[0];
93751             var hc = null;
93752             var tc = null;
93753             var hy = null;
93754             var ty = null;
93755             var C = null;
93756             var c = null;
93757             var U = null;
93758             var u = null;
93759             C = this._hi / y._hi;
93760             c = DD.SPLIT * C;
93761             hc = c - C;
93762             u = DD.SPLIT * y._hi;
93763             hc = c - hc;
93764             tc = C - hc;
93765             hy = u - y._hi;
93766             U = C * y._hi;
93767             hy = u - hy;
93768             ty = y._hi - hy;
93769             u = hc * hy - U + hc * ty + tc * hy + tc * ty;
93770             c = (this._hi - U - u + this._lo - C * y._lo) / y._hi;
93771             u = C + c;
93772             var zhi = u;
93773             var zlo = C - u + c;
93774             return new DD(zhi, zlo)
93775           } else if (typeof arguments[0] === 'number') {
93776             var y$1 = arguments[0];
93777             if (Double.isNaN(y$1)) { return DD.createNaN() }
93778             return DD.copy(this).selfDivide(y$1, 0.0)
93779           }
93780         };
93781         DD.prototype.ge = function ge (y) {
93782           return (this._hi > y._hi || this._hi === y._hi) && this._lo >= y._lo
93783         };
93784         DD.prototype.pow = function pow (exp) {
93785           if (exp === 0.0) { return DD.valueOf(1.0) }
93786           var r = new DD(this);
93787           var s = DD.valueOf(1.0);
93788           var n = Math.abs(exp);
93789           if (n > 1) {
93790             while (n > 0) {
93791               if (n % 2 === 1) {
93792                 s.selfMultiply(r);
93793               }
93794               n /= 2;
93795               if (n > 0) { r = r.sqr(); }
93796             }
93797           } else {
93798             s = r;
93799           }
93800           if (exp < 0) { return s.reciprocal() }
93801           return s
93802         };
93803         DD.prototype.ceil = function ceil () {
93804           if (this.isNaN()) { return DD.NaN }
93805           var fhi = Math.ceil(this._hi);
93806           var flo = 0.0;
93807           if (fhi === this._hi) {
93808             flo = Math.ceil(this._lo);
93809           }
93810           return new DD(fhi, flo)
93811         };
93812         DD.prototype.compareTo = function compareTo (o) {
93813           var other = o;
93814           if (this._hi < other._hi) { return -1 }
93815           if (this._hi > other._hi) { return 1 }
93816           if (this._lo < other._lo) { return -1 }
93817           if (this._lo > other._lo) { return 1 }
93818           return 0
93819         };
93820         DD.prototype.rint = function rint () {
93821           if (this.isNaN()) { return this }
93822           var plus5 = this.add(0.5);
93823           return plus5.floor()
93824         };
93825         DD.prototype.setValue = function setValue () {
93826           if (arguments[0] instanceof DD) {
93827             var value = arguments[0];
93828             this.init(value);
93829             return this
93830           } else if (typeof arguments[0] === 'number') {
93831             var value$1 = arguments[0];
93832             this.init(value$1);
93833             return this
93834           }
93835         };
93836         DD.prototype.max = function max (x) {
93837           if (this.ge(x)) {
93838             return this
93839           } else {
93840             return x
93841           }
93842         };
93843         DD.prototype.sqrt = function sqrt () {
93844           if (this.isZero()) { return DD.valueOf(0.0) }
93845           if (this.isNegative()) {
93846             return DD.NaN
93847           }
93848           var x = 1.0 / Math.sqrt(this._hi);
93849           var ax = this._hi * x;
93850           var axdd = DD.valueOf(ax);
93851           var diffSq = this.subtract(axdd.sqr());
93852           var d2 = diffSq._hi * (x * 0.5);
93853           return axdd.add(d2)
93854         };
93855         DD.prototype.selfAdd = function selfAdd () {
93856           if (arguments.length === 1) {
93857             if (arguments[0] instanceof DD) {
93858               var y = arguments[0];
93859               return this.selfAdd(y._hi, y._lo)
93860             } else if (typeof arguments[0] === 'number') {
93861               var y$1 = arguments[0];
93862               var H = null;
93863               var h = null;
93864               var S = null;
93865               var s = null;
93866               var e = null;
93867               var f = null;
93868               S = this._hi + y$1;
93869               e = S - this._hi;
93870               s = S - e;
93871               s = y$1 - e + (this._hi - s);
93872               f = s + this._lo;
93873               H = S + f;
93874               h = f + (S - H);
93875               this._hi = H + h;
93876               this._lo = h + (H - this._hi);
93877               return this
93878             }
93879           } else if (arguments.length === 2) {
93880             var yhi = arguments[0];
93881             var ylo = arguments[1];
93882             var H$1 = null;
93883             var h$1 = null;
93884             var T = null;
93885             var t = null;
93886             var S$1 = null;
93887             var s$1 = null;
93888             var e$1 = null;
93889             var f$1 = null;
93890             S$1 = this._hi + yhi;
93891             T = this._lo + ylo;
93892             e$1 = S$1 - this._hi;
93893             f$1 = T - this._lo;
93894             s$1 = S$1 - e$1;
93895             t = T - f$1;
93896             s$1 = yhi - e$1 + (this._hi - s$1);
93897             t = ylo - f$1 + (this._lo - t);
93898             e$1 = s$1 + T;
93899             H$1 = S$1 + e$1;
93900             h$1 = e$1 + (S$1 - H$1);
93901             e$1 = t + h$1;
93902             var zhi = H$1 + e$1;
93903             var zlo = e$1 + (H$1 - zhi);
93904             this._hi = zhi;
93905             this._lo = zlo;
93906             return this
93907           }
93908         };
93909         DD.prototype.selfMultiply = function selfMultiply () {
93910           if (arguments.length === 1) {
93911             if (arguments[0] instanceof DD) {
93912               var y = arguments[0];
93913               return this.selfMultiply(y._hi, y._lo)
93914             } else if (typeof arguments[0] === 'number') {
93915               var y$1 = arguments[0];
93916               return this.selfMultiply(y$1, 0.0)
93917             }
93918           } else if (arguments.length === 2) {
93919             var yhi = arguments[0];
93920             var ylo = arguments[1];
93921             var hx = null;
93922             var tx = null;
93923             var hy = null;
93924             var ty = null;
93925             var C = null;
93926             var c = null;
93927             C = DD.SPLIT * this._hi;
93928             hx = C - this._hi;
93929             c = DD.SPLIT * yhi;
93930             hx = C - hx;
93931             tx = this._hi - hx;
93932             hy = c - yhi;
93933             C = this._hi * yhi;
93934             hy = c - hy;
93935             ty = yhi - hy;
93936             c = hx * hy - C + hx * ty + tx * hy + tx * ty + (this._hi * ylo + this._lo * yhi);
93937             var zhi = C + c;
93938             hx = C - zhi;
93939             var zlo = c + hx;
93940             this._hi = zhi;
93941             this._lo = zlo;
93942             return this
93943           }
93944         };
93945         DD.prototype.selfSqr = function selfSqr () {
93946           return this.selfMultiply(this)
93947         };
93948         DD.prototype.floor = function floor () {
93949           if (this.isNaN()) { return DD.NaN }
93950           var fhi = Math.floor(this._hi);
93951           var flo = 0.0;
93952           if (fhi === this._hi) {
93953             flo = Math.floor(this._lo);
93954           }
93955           return new DD(fhi, flo)
93956         };
93957         DD.prototype.negate = function negate () {
93958           if (this.isNaN()) { return this }
93959           return new DD(-this._hi, -this._lo)
93960         };
93961         DD.prototype.clone = function clone () {
93962           // try {
93963           // return null
93964           // } catch (ex) {
93965           // if (ex instanceof CloneNotSupportedException) {
93966           //   return null
93967           // } else throw ex
93968           // } finally {}
93969         };
93970         DD.prototype.multiply = function multiply () {
93971           if (arguments[0] instanceof DD) {
93972             var y = arguments[0];
93973             if (y.isNaN()) { return DD.createNaN() }
93974             return DD.copy(this).selfMultiply(y)
93975           } else if (typeof arguments[0] === 'number') {
93976             var y$1 = arguments[0];
93977             if (Double.isNaN(y$1)) { return DD.createNaN() }
93978             return DD.copy(this).selfMultiply(y$1, 0.0)
93979           }
93980         };
93981         DD.prototype.isNaN = function isNaN () {
93982           return Double.isNaN(this._hi)
93983         };
93984         DD.prototype.intValue = function intValue () {
93985           return Math.trunc(this._hi)
93986         };
93987         DD.prototype.toString = function toString () {
93988           var mag = DD.magnitude(this._hi);
93989           if (mag >= -3 && mag <= 20) { return this.toStandardNotation() }
93990           return this.toSciNotation()
93991         };
93992         DD.prototype.toStandardNotation = function toStandardNotation () {
93993           var specialStr = this.getSpecialNumberString();
93994           if (specialStr !== null) { return specialStr }
93995           var magnitude = new Array(1).fill(null);
93996           var sigDigits = this.extractSignificantDigits(true, magnitude);
93997           var decimalPointPos = magnitude[0] + 1;
93998           var num = sigDigits;
93999           if (sigDigits.charAt(0) === '.') {
94000             num = '0' + sigDigits;
94001           } else if (decimalPointPos < 0) {
94002             num = '0.' + DD.stringOfChar('0', -decimalPointPos) + sigDigits;
94003           } else if (sigDigits.indexOf('.') === -1) {
94004             var numZeroes = decimalPointPos - sigDigits.length;
94005             var zeroes = DD.stringOfChar('0', numZeroes);
94006             num = sigDigits + zeroes + '.0';
94007           }
94008           if (this.isNegative()) { return '-' + num }
94009           return num
94010         };
94011         DD.prototype.reciprocal = function reciprocal () {
94012           var hc = null;
94013           var tc = null;
94014           var hy = null;
94015           var ty = null;
94016           var C = null;
94017           var c = null;
94018           var U = null;
94019           var u = null;
94020           C = 1.0 / this._hi;
94021           c = DD.SPLIT * C;
94022           hc = c - C;
94023           u = DD.SPLIT * this._hi;
94024           hc = c - hc;
94025           tc = C - hc;
94026           hy = u - this._hi;
94027           U = C * this._hi;
94028           hy = u - hy;
94029           ty = this._hi - hy;
94030           u = hc * hy - U + hc * ty + tc * hy + tc * ty;
94031           c = (1.0 - U - u - C * this._lo) / this._hi;
94032           var zhi = C + c;
94033           var zlo = C - zhi + c;
94034           return new DD(zhi, zlo)
94035         };
94036         DD.prototype.toSciNotation = function toSciNotation () {
94037           if (this.isZero()) { return DD.SCI_NOT_ZERO }
94038           var specialStr = this.getSpecialNumberString();
94039           if (specialStr !== null) { return specialStr }
94040           var magnitude = new Array(1).fill(null);
94041           var digits = this.extractSignificantDigits(false, magnitude);
94042           var expStr = DD.SCI_NOT_EXPONENT_CHAR + magnitude[0];
94043           if (digits.charAt(0) === '0') {
94044             throw new Error('Found leading zero: ' + digits)
94045           }
94046           var trailingDigits = '';
94047           if (digits.length > 1) { trailingDigits = digits.substring(1); }
94048           var digitsWithDecimal = digits.charAt(0) + '.' + trailingDigits;
94049           if (this.isNegative()) { return '-' + digitsWithDecimal + expStr }
94050           return digitsWithDecimal + expStr
94051         };
94052         DD.prototype.abs = function abs () {
94053           if (this.isNaN()) { return DD.NaN }
94054           if (this.isNegative()) { return this.negate() }
94055           return new DD(this)
94056         };
94057         DD.prototype.isPositive = function isPositive () {
94058           return (this._hi > 0.0 || this._hi === 0.0) && this._lo > 0.0
94059         };
94060         DD.prototype.lt = function lt (y) {
94061           return (this._hi < y._hi || this._hi === y._hi) && this._lo < y._lo
94062         };
94063         DD.prototype.add = function add () {
94064           if (arguments[0] instanceof DD) {
94065             var y = arguments[0];
94066             return DD.copy(this).selfAdd(y)
94067           } else if (typeof arguments[0] === 'number') {
94068             var y$1 = arguments[0];
94069             return DD.copy(this).selfAdd(y$1)
94070           }
94071         };
94072         DD.prototype.init = function init () {
94073           if (arguments.length === 1) {
94074             if (typeof arguments[0] === 'number') {
94075               var x = arguments[0];
94076               this._hi = x;
94077               this._lo = 0.0;
94078             } else if (arguments[0] instanceof DD) {
94079               var dd = arguments[0];
94080               this._hi = dd._hi;
94081               this._lo = dd._lo;
94082             }
94083           } else if (arguments.length === 2) {
94084             var hi = arguments[0];
94085             var lo = arguments[1];
94086             this._hi = hi;
94087             this._lo = lo;
94088           }
94089         };
94090         DD.prototype.gt = function gt (y) {
94091           return (this._hi > y._hi || this._hi === y._hi) && this._lo > y._lo
94092         };
94093         DD.prototype.isNegative = function isNegative () {
94094           return (this._hi < 0.0 || this._hi === 0.0) && this._lo < 0.0
94095         };
94096         DD.prototype.trunc = function trunc () {
94097           if (this.isNaN()) { return DD.NaN }
94098           if (this.isPositive()) { return this.floor(); } else { return this.ceil() }
94099         };
94100         DD.prototype.signum = function signum () {
94101           if (this._hi > 0) { return 1 }
94102           if (this._hi < 0) { return -1 }
94103           if (this._lo > 0) { return 1 }
94104           if (this._lo < 0) { return -1 }
94105           return 0
94106         };
94107         DD.prototype.interfaces_ = function interfaces_ () {
94108           return [Serializable, Comparable, Clonable]
94109         };
94110         DD.prototype.getClass = function getClass () {
94111           return DD
94112         };
94113         DD.sqr = function sqr (x) {
94114           return DD.valueOf(x).selfMultiply(x)
94115         };
94116         DD.valueOf = function valueOf () {
94117           if (typeof arguments[0] === 'string') {
94118             var str = arguments[0];
94119             return DD.parse(str)
94120           } else if (typeof arguments[0] === 'number') {
94121             var x = arguments[0];
94122             return new DD(x)
94123           }
94124         };
94125         DD.sqrt = function sqrt (x) {
94126           return DD.valueOf(x).sqrt()
94127         };
94128         DD.parse = function parse (str) {
94129           var i = 0;
94130           var strlen = str.length;
94131           while (Character.isWhitespace(str.charAt(i))) { i++; }
94132           var isNegative = false;
94133           if (i < strlen) {
94134             var signCh = str.charAt(i);
94135             if (signCh === '-' || signCh === '+') {
94136               i++;
94137               if (signCh === '-') { isNegative = true; }
94138             }
94139           }
94140           var val = new DD();
94141           var numDigits = 0;
94142           var numBeforeDec = 0;
94143           var exp = 0;
94144           while (true) {
94145             if (i >= strlen) { break }
94146             var ch = str.charAt(i);
94147             i++;
94148             if (Character.isDigit(ch)) {
94149               var d = ch - '0';
94150               val.selfMultiply(DD.TEN);
94151               val.selfAdd(d);
94152               numDigits++;
94153               continue
94154             }
94155             if (ch === '.') {
94156               numBeforeDec = numDigits;
94157               continue
94158             }
94159             if (ch === 'e' || ch === 'E') {
94160               var expStr = str.substring(i);
94161               try {
94162                 exp = Integer.parseInt(expStr);
94163               } catch (ex) {
94164                 if (ex instanceof Error) {
94165                   throw new Error('Invalid exponent ' + expStr + ' in string ' + str)
94166                 } else { throw ex }
94167               } finally {}
94168               break
94169             }
94170             throw new Error("Unexpected character '" + ch + "' at position " + i + ' in string ' + str)
94171           }
94172           var val2 = val;
94173           var numDecPlaces = numDigits - numBeforeDec - exp;
94174           if (numDecPlaces === 0) {
94175             val2 = val;
94176           } else if (numDecPlaces > 0) {
94177             var scale = DD.TEN.pow(numDecPlaces);
94178             val2 = val.divide(scale);
94179           } else if (numDecPlaces < 0) {
94180             var scale$1 = DD.TEN.pow(-numDecPlaces);
94181             val2 = val.multiply(scale$1);
94182           }
94183           if (isNegative) {
94184             return val2.negate()
94185           }
94186           return val2
94187         };
94188         DD.createNaN = function createNaN () {
94189           return new DD(Double.NaN, Double.NaN)
94190         };
94191         DD.copy = function copy (dd) {
94192           return new DD(dd)
94193         };
94194         DD.magnitude = function magnitude (x) {
94195           var xAbs = Math.abs(x);
94196           var xLog10 = Math.log(xAbs) / Math.log(10);
94197           var xMag = Math.trunc(Math.floor(xLog10));
94198           var xApprox = Math.pow(10, xMag);
94199           if (xApprox * 10 <= xAbs) { xMag += 1; }
94200           return xMag
94201         };
94202         DD.stringOfChar = function stringOfChar (ch, len) {
94203           var buf = new StringBuffer();
94204           for (var i = 0; i < len; i++) {
94205             buf.append(ch);
94206           }
94207           return buf.toString()
94208         };
94209         staticAccessors$7.PI.get = function () { return new DD(3.141592653589793116e+00, 1.224646799147353207e-16) };
94210         staticAccessors$7.TWO_PI.get = function () { return new DD(6.283185307179586232e+00, 2.449293598294706414e-16) };
94211         staticAccessors$7.PI_2.get = function () { return new DD(1.570796326794896558e+00, 6.123233995736766036e-17) };
94212         staticAccessors$7.E.get = function () { return new DD(2.718281828459045091e+00, 1.445646891729250158e-16) };
94213         staticAccessors$7.NaN.get = function () { return new DD(Double.NaN, Double.NaN) };
94214         staticAccessors$7.EPS.get = function () { return 1.23259516440783e-32 };
94215         staticAccessors$7.SPLIT.get = function () { return 134217729.0 };
94216         staticAccessors$7.MAX_PRINT_DIGITS.get = function () { return 32 };
94217         staticAccessors$7.TEN.get = function () { return DD.valueOf(10.0) };
94218         staticAccessors$7.ONE.get = function () { return DD.valueOf(1.0) };
94219         staticAccessors$7.SCI_NOT_EXPONENT_CHAR.get = function () { return 'E' };
94220         staticAccessors$7.SCI_NOT_ZERO.get = function () { return '0.0E0' };
94221
94222         Object.defineProperties( DD, staticAccessors$7 );
94223
94224         var CGAlgorithmsDD = function CGAlgorithmsDD () {};
94225
94226         var staticAccessors$6 = { DP_SAFE_EPSILON: { configurable: true } };
94227
94228         CGAlgorithmsDD.prototype.interfaces_ = function interfaces_ () {
94229           return []
94230         };
94231         CGAlgorithmsDD.prototype.getClass = function getClass () {
94232           return CGAlgorithmsDD
94233         };
94234         CGAlgorithmsDD.orientationIndex = function orientationIndex (p1, p2, q) {
94235           var index = CGAlgorithmsDD.orientationIndexFilter(p1, p2, q);
94236           if (index <= 1) { return index }
94237           var dx1 = DD.valueOf(p2.x).selfAdd(-p1.x);
94238           var dy1 = DD.valueOf(p2.y).selfAdd(-p1.y);
94239           var dx2 = DD.valueOf(q.x).selfAdd(-p2.x);
94240           var dy2 = DD.valueOf(q.y).selfAdd(-p2.y);
94241           return dx1.selfMultiply(dy2).selfSubtract(dy1.selfMultiply(dx2)).signum()
94242         };
94243         CGAlgorithmsDD.signOfDet2x2 = function signOfDet2x2 (x1, y1, x2, y2) {
94244           var det = x1.multiply(y2).selfSubtract(y1.multiply(x2));
94245           return det.signum()
94246         };
94247         CGAlgorithmsDD.intersection = function intersection (p1, p2, q1, q2) {
94248           var denom1 = DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(DD.valueOf(p2.x).selfSubtract(p1.x));
94249           var denom2 = DD.valueOf(q2.x).selfSubtract(q1.x).selfMultiply(DD.valueOf(p2.y).selfSubtract(p1.y));
94250           var denom = denom1.subtract(denom2);
94251           var numx1 = DD.valueOf(q2.x).selfSubtract(q1.x).selfMultiply(DD.valueOf(p1.y).selfSubtract(q1.y));
94252           var numx2 = DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(DD.valueOf(p1.x).selfSubtract(q1.x));
94253           var numx = numx1.subtract(numx2);
94254           var fracP = numx.selfDivide(denom).doubleValue();
94255           var x = DD.valueOf(p1.x).selfAdd(DD.valueOf(p2.x).selfSubtract(p1.x).selfMultiply(fracP)).doubleValue();
94256           var numy1 = DD.valueOf(p2.x).selfSubtract(p1.x).selfMultiply(DD.valueOf(p1.y).selfSubtract(q1.y));
94257           var numy2 = DD.valueOf(p2.y).selfSubtract(p1.y).selfMultiply(DD.valueOf(p1.x).selfSubtract(q1.x));
94258           var numy = numy1.subtract(numy2);
94259           var fracQ = numy.selfDivide(denom).doubleValue();
94260           var y = DD.valueOf(q1.y).selfAdd(DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(fracQ)).doubleValue();
94261           return new Coordinate(x, y)
94262         };
94263         CGAlgorithmsDD.orientationIndexFilter = function orientationIndexFilter (pa, pb, pc) {
94264           var detsum = null;
94265           var detleft = (pa.x - pc.x) * (pb.y - pc.y);
94266           var detright = (pa.y - pc.y) * (pb.x - pc.x);
94267           var det = detleft - detright;
94268           if (detleft > 0.0) {
94269             if (detright <= 0.0) {
94270               return CGAlgorithmsDD.signum(det)
94271             } else {
94272               detsum = detleft + detright;
94273             }
94274           } else if (detleft < 0.0) {
94275             if (detright >= 0.0) {
94276               return CGAlgorithmsDD.signum(det)
94277             } else {
94278               detsum = -detleft - detright;
94279             }
94280           } else {
94281             return CGAlgorithmsDD.signum(det)
94282           }
94283           var errbound = CGAlgorithmsDD.DP_SAFE_EPSILON * detsum;
94284           if (det >= errbound || -det >= errbound) {
94285             return CGAlgorithmsDD.signum(det)
94286           }
94287           return 2
94288         };
94289         CGAlgorithmsDD.signum = function signum (x) {
94290           if (x > 0) { return 1 }
94291           if (x < 0) { return -1 }
94292           return 0
94293         };
94294         staticAccessors$6.DP_SAFE_EPSILON.get = function () { return 1e-15 };
94295
94296         Object.defineProperties( CGAlgorithmsDD, staticAccessors$6 );
94297
94298         var CoordinateSequence = function CoordinateSequence () {};
94299
94300         var staticAccessors$8 = { X: { configurable: true },Y: { configurable: true },Z: { configurable: true },M: { configurable: true } };
94301
94302         staticAccessors$8.X.get = function () { return 0 };
94303         staticAccessors$8.Y.get = function () { return 1 };
94304         staticAccessors$8.Z.get = function () { return 2 };
94305         staticAccessors$8.M.get = function () { return 3 };
94306         CoordinateSequence.prototype.setOrdinate = function setOrdinate (index, ordinateIndex, value) {};
94307         CoordinateSequence.prototype.size = function size () {};
94308         CoordinateSequence.prototype.getOrdinate = function getOrdinate (index, ordinateIndex) {};
94309         CoordinateSequence.prototype.getCoordinate = function getCoordinate () {};
94310         CoordinateSequence.prototype.getCoordinateCopy = function getCoordinateCopy (i) {};
94311         CoordinateSequence.prototype.getDimension = function getDimension () {};
94312         CoordinateSequence.prototype.getX = function getX (index) {};
94313         CoordinateSequence.prototype.clone = function clone () {};
94314         CoordinateSequence.prototype.expandEnvelope = function expandEnvelope (env) {};
94315         CoordinateSequence.prototype.copy = function copy () {};
94316         CoordinateSequence.prototype.getY = function getY (index) {};
94317         CoordinateSequence.prototype.toCoordinateArray = function toCoordinateArray () {};
94318         CoordinateSequence.prototype.interfaces_ = function interfaces_ () {
94319           return [Clonable]
94320         };
94321         CoordinateSequence.prototype.getClass = function getClass () {
94322           return CoordinateSequence
94323         };
94324
94325         Object.defineProperties( CoordinateSequence, staticAccessors$8 );
94326
94327         var Exception = function Exception () {};
94328
94329         var NotRepresentableException = (function (Exception$$1) {
94330           function NotRepresentableException () {
94331             Exception$$1.call(this, 'Projective point not representable on the Cartesian plane.');
94332           }
94333
94334           if ( Exception$$1 ) NotRepresentableException.__proto__ = Exception$$1;
94335           NotRepresentableException.prototype = Object.create( Exception$$1 && Exception$$1.prototype );
94336           NotRepresentableException.prototype.constructor = NotRepresentableException;
94337           NotRepresentableException.prototype.interfaces_ = function interfaces_ () {
94338             return []
94339           };
94340           NotRepresentableException.prototype.getClass = function getClass () {
94341             return NotRepresentableException
94342           };
94343
94344           return NotRepresentableException;
94345         }(Exception));
94346
94347         var System = function System () {};
94348
94349         System.arraycopy = function arraycopy (src, srcPos, dest, destPos, len) {
94350           var c = 0;
94351           for (var i = srcPos; i < srcPos + len; i++) {
94352             dest[destPos + c] = src[i];
94353             c++;
94354           }
94355         };
94356
94357         System.getProperty = function getProperty (name) {
94358           return {
94359             'line.separator': '\n'
94360           }[name]
94361         };
94362
94363         var HCoordinate = function HCoordinate () {
94364           this.x = null;
94365           this.y = null;
94366           this.w = null;
94367           if (arguments.length === 0) {
94368             this.x = 0.0;
94369             this.y = 0.0;
94370             this.w = 1.0;
94371           } else if (arguments.length === 1) {
94372             var p = arguments[0];
94373             this.x = p.x;
94374             this.y = p.y;
94375             this.w = 1.0;
94376           } else if (arguments.length === 2) {
94377             if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
94378               var _x = arguments[0];
94379               var _y = arguments[1];
94380               this.x = _x;
94381               this.y = _y;
94382               this.w = 1.0;
94383             } else if (arguments[0] instanceof HCoordinate && arguments[1] instanceof HCoordinate) {
94384               var p1 = arguments[0];
94385               var p2 = arguments[1];
94386               this.x = p1.y * p2.w - p2.y * p1.w;
94387               this.y = p2.x * p1.w - p1.x * p2.w;
94388               this.w = p1.x * p2.y - p2.x * p1.y;
94389             } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
94390               var p1$1 = arguments[0];
94391               var p2$1 = arguments[1];
94392               this.x = p1$1.y - p2$1.y;
94393               this.y = p2$1.x - p1$1.x;
94394               this.w = p1$1.x * p2$1.y - p2$1.x * p1$1.y;
94395             }
94396           } else if (arguments.length === 3) {
94397             var _x$1 = arguments[0];
94398             var _y$1 = arguments[1];
94399             var _w = arguments[2];
94400             this.x = _x$1;
94401             this.y = _y$1;
94402             this.w = _w;
94403           } else if (arguments.length === 4) {
94404             var p1$2 = arguments[0];
94405             var p2$2 = arguments[1];
94406             var q1 = arguments[2];
94407             var q2 = arguments[3];
94408             var px = p1$2.y - p2$2.y;
94409             var py = p2$2.x - p1$2.x;
94410             var pw = p1$2.x * p2$2.y - p2$2.x * p1$2.y;
94411             var qx = q1.y - q2.y;
94412             var qy = q2.x - q1.x;
94413             var qw = q1.x * q2.y - q2.x * q1.y;
94414             this.x = py * qw - qy * pw;
94415             this.y = qx * pw - px * qw;
94416             this.w = px * qy - qx * py;
94417           }
94418         };
94419         HCoordinate.prototype.getY = function getY () {
94420           var a = this.y / this.w;
94421           if (Double.isNaN(a) || Double.isInfinite(a)) {
94422             throw new NotRepresentableException()
94423           }
94424           return a
94425         };
94426         HCoordinate.prototype.getX = function getX () {
94427           var a = this.x / this.w;
94428           if (Double.isNaN(a) || Double.isInfinite(a)) {
94429             throw new NotRepresentableException()
94430           }
94431           return a
94432         };
94433         HCoordinate.prototype.getCoordinate = function getCoordinate () {
94434           var p = new Coordinate();
94435           p.x = this.getX();
94436           p.y = this.getY();
94437           return p
94438         };
94439         HCoordinate.prototype.interfaces_ = function interfaces_ () {
94440           return []
94441         };
94442         HCoordinate.prototype.getClass = function getClass () {
94443           return HCoordinate
94444         };
94445         HCoordinate.intersection = function intersection (p1, p2, q1, q2) {
94446           var px = p1.y - p2.y;
94447           var py = p2.x - p1.x;
94448           var pw = p1.x * p2.y - p2.x * p1.y;
94449           var qx = q1.y - q2.y;
94450           var qy = q2.x - q1.x;
94451           var qw = q1.x * q2.y - q2.x * q1.y;
94452           var x = py * qw - qy * pw;
94453           var y = qx * pw - px * qw;
94454           var w = px * qy - qx * py;
94455           var xInt = x / w;
94456           var yInt = y / w;
94457           if (Double.isNaN(xInt) || (Double.isInfinite(xInt) || Double.isNaN(yInt)) || Double.isInfinite(yInt)) {
94458             throw new NotRepresentableException()
94459           }
94460           return new Coordinate(xInt, yInt)
94461         };
94462
94463         var Envelope = function Envelope () {
94464           this._minx = null;
94465           this._maxx = null;
94466           this._miny = null;
94467           this._maxy = null;
94468           if (arguments.length === 0) {
94469             this.init();
94470           } else if (arguments.length === 1) {
94471             if (arguments[0] instanceof Coordinate) {
94472               var p = arguments[0];
94473               this.init(p.x, p.x, p.y, p.y);
94474             } else if (arguments[0] instanceof Envelope) {
94475               var env = arguments[0];
94476               this.init(env);
94477             }
94478           } else if (arguments.length === 2) {
94479             var p1 = arguments[0];
94480             var p2 = arguments[1];
94481             this.init(p1.x, p2.x, p1.y, p2.y);
94482           } else if (arguments.length === 4) {
94483             var x1 = arguments[0];
94484             var x2 = arguments[1];
94485             var y1 = arguments[2];
94486             var y2 = arguments[3];
94487             this.init(x1, x2, y1, y2);
94488           }
94489         };
94490
94491         var staticAccessors$9 = { serialVersionUID: { configurable: true } };
94492         Envelope.prototype.getArea = function getArea () {
94493           return this.getWidth() * this.getHeight()
94494         };
94495         Envelope.prototype.equals = function equals (other) {
94496           if (!(other instanceof Envelope)) {
94497             return false
94498           }
94499           var otherEnvelope = other;
94500           if (this.isNull()) {
94501             return otherEnvelope.isNull()
94502           }
94503           return this._maxx === otherEnvelope.getMaxX() && this._maxy === otherEnvelope.getMaxY() && this._minx === otherEnvelope.getMinX() && this._miny === otherEnvelope.getMinY()
94504         };
94505         Envelope.prototype.intersection = function intersection (env) {
94506           if (this.isNull() || env.isNull() || !this.intersects(env)) { return new Envelope() }
94507           var intMinX = this._minx > env._minx ? this._minx : env._minx;
94508           var intMinY = this._miny > env._miny ? this._miny : env._miny;
94509           var intMaxX = this._maxx < env._maxx ? this._maxx : env._maxx;
94510           var intMaxY = this._maxy < env._maxy ? this._maxy : env._maxy;
94511           return new Envelope(intMinX, intMaxX, intMinY, intMaxY)
94512         };
94513         Envelope.prototype.isNull = function isNull () {
94514           return this._maxx < this._minx
94515         };
94516         Envelope.prototype.getMaxX = function getMaxX () {
94517           return this._maxx
94518         };
94519         Envelope.prototype.covers = function covers () {
94520           if (arguments.length === 1) {
94521             if (arguments[0] instanceof Coordinate) {
94522               var p = arguments[0];
94523               return this.covers(p.x, p.y)
94524             } else if (arguments[0] instanceof Envelope) {
94525               var other = arguments[0];
94526               if (this.isNull() || other.isNull()) {
94527                 return false
94528               }
94529               return other.getMinX() >= this._minx && other.getMaxX() <= this._maxx && other.getMinY() >= this._miny && other.getMaxY() <= this._maxy
94530             }
94531           } else if (arguments.length === 2) {
94532             var x = arguments[0];
94533             var y = arguments[1];
94534             if (this.isNull()) { return false }
94535             return x >= this._minx && x <= this._maxx && y >= this._miny && y <= this._maxy
94536           }
94537         };
94538         Envelope.prototype.intersects = function intersects () {
94539           if (arguments.length === 1) {
94540             if (arguments[0] instanceof Envelope) {
94541               var other = arguments[0];
94542               if (this.isNull() || other.isNull()) {
94543                 return false
94544               }
94545               return !(other._minx > this._maxx || other._maxx < this._minx || other._miny > this._maxy || other._maxy < this._miny)
94546             } else if (arguments[0] instanceof Coordinate) {
94547               var p = arguments[0];
94548               return this.intersects(p.x, p.y)
94549             }
94550           } else if (arguments.length === 2) {
94551             var x = arguments[0];
94552             var y = arguments[1];
94553             if (this.isNull()) { return false }
94554             return !(x > this._maxx || x < this._minx || y > this._maxy || y < this._miny)
94555           }
94556         };
94557         Envelope.prototype.getMinY = function getMinY () {
94558           return this._miny
94559         };
94560         Envelope.prototype.getMinX = function getMinX () {
94561           return this._minx
94562         };
94563         Envelope.prototype.expandToInclude = function expandToInclude () {
94564           if (arguments.length === 1) {
94565             if (arguments[0] instanceof Coordinate) {
94566               var p = arguments[0];
94567               this.expandToInclude(p.x, p.y);
94568             } else if (arguments[0] instanceof Envelope) {
94569               var other = arguments[0];
94570               if (other.isNull()) {
94571                 return null
94572               }
94573               if (this.isNull()) {
94574                 this._minx = other.getMinX();
94575                 this._maxx = other.getMaxX();
94576                 this._miny = other.getMinY();
94577                 this._maxy = other.getMaxY();
94578               } else {
94579                 if (other._minx < this._minx) {
94580                   this._minx = other._minx;
94581                 }
94582                 if (other._maxx > this._maxx) {
94583                   this._maxx = other._maxx;
94584                 }
94585                 if (other._miny < this._miny) {
94586                   this._miny = other._miny;
94587                 }
94588                 if (other._maxy > this._maxy) {
94589                   this._maxy = other._maxy;
94590                 }
94591               }
94592             }
94593           } else if (arguments.length === 2) {
94594             var x = arguments[0];
94595             var y = arguments[1];
94596             if (this.isNull()) {
94597               this._minx = x;
94598               this._maxx = x;
94599               this._miny = y;
94600               this._maxy = y;
94601             } else {
94602               if (x < this._minx) {
94603                 this._minx = x;
94604               }
94605               if (x > this._maxx) {
94606                 this._maxx = x;
94607               }
94608               if (y < this._miny) {
94609                 this._miny = y;
94610               }
94611               if (y > this._maxy) {
94612                 this._maxy = y;
94613               }
94614             }
94615           }
94616         };
94617         Envelope.prototype.minExtent = function minExtent () {
94618           if (this.isNull()) { return 0.0 }
94619           var w = this.getWidth();
94620           var h = this.getHeight();
94621           if (w < h) { return w }
94622           return h
94623         };
94624         Envelope.prototype.getWidth = function getWidth () {
94625           if (this.isNull()) {
94626             return 0
94627           }
94628           return this._maxx - this._minx
94629         };
94630         Envelope.prototype.compareTo = function compareTo (o) {
94631           var env = o;
94632           if (this.isNull()) {
94633             if (env.isNull()) { return 0 }
94634             return -1
94635           } else {
94636             if (env.isNull()) { return 1 }
94637           }
94638           if (this._minx < env._minx) { return -1 }
94639           if (this._minx > env._minx) { return 1 }
94640           if (this._miny < env._miny) { return -1 }
94641           if (this._miny > env._miny) { return 1 }
94642           if (this._maxx < env._maxx) { return -1 }
94643           if (this._maxx > env._maxx) { return 1 }
94644           if (this._maxy < env._maxy) { return -1 }
94645           if (this._maxy > env._maxy) { return 1 }
94646           return 0
94647         };
94648         Envelope.prototype.translate = function translate (transX, transY) {
94649           if (this.isNull()) {
94650             return null
94651           }
94652           this.init(this.getMinX() + transX, this.getMaxX() + transX, this.getMinY() + transY, this.getMaxY() + transY);
94653         };
94654         Envelope.prototype.toString = function toString () {
94655           return 'Env[' + this._minx + ' : ' + this._maxx + ', ' + this._miny + ' : ' + this._maxy + ']'
94656         };
94657         Envelope.prototype.setToNull = function setToNull () {
94658           this._minx = 0;
94659           this._maxx = -1;
94660           this._miny = 0;
94661           this._maxy = -1;
94662         };
94663         Envelope.prototype.getHeight = function getHeight () {
94664           if (this.isNull()) {
94665             return 0
94666           }
94667           return this._maxy - this._miny
94668         };
94669         Envelope.prototype.maxExtent = function maxExtent () {
94670           if (this.isNull()) { return 0.0 }
94671           var w = this.getWidth();
94672           var h = this.getHeight();
94673           if (w > h) { return w }
94674           return h
94675         };
94676         Envelope.prototype.expandBy = function expandBy () {
94677           if (arguments.length === 1) {
94678             var distance = arguments[0];
94679             this.expandBy(distance, distance);
94680           } else if (arguments.length === 2) {
94681             var deltaX = arguments[0];
94682             var deltaY = arguments[1];
94683             if (this.isNull()) { return null }
94684             this._minx -= deltaX;
94685             this._maxx += deltaX;
94686             this._miny -= deltaY;
94687             this._maxy += deltaY;
94688             if (this._minx > this._maxx || this._miny > this._maxy) { this.setToNull(); }
94689           }
94690         };
94691         Envelope.prototype.contains = function contains () {
94692           if (arguments.length === 1) {
94693             if (arguments[0] instanceof Envelope) {
94694               var other = arguments[0];
94695               return this.covers(other)
94696             } else if (arguments[0] instanceof Coordinate) {
94697               var p = arguments[0];
94698               return this.covers(p)
94699             }
94700           } else if (arguments.length === 2) {
94701             var x = arguments[0];
94702             var y = arguments[1];
94703             return this.covers(x, y)
94704           }
94705         };
94706         Envelope.prototype.centre = function centre () {
94707           if (this.isNull()) { return null }
94708           return new Coordinate((this.getMinX() + this.getMaxX()) / 2.0, (this.getMinY() + this.getMaxY()) / 2.0)
94709         };
94710         Envelope.prototype.init = function init () {
94711           if (arguments.length === 0) {
94712             this.setToNull();
94713           } else if (arguments.length === 1) {
94714             if (arguments[0] instanceof Coordinate) {
94715               var p = arguments[0];
94716               this.init(p.x, p.x, p.y, p.y);
94717             } else if (arguments[0] instanceof Envelope) {
94718               var env = arguments[0];
94719               this._minx = env._minx;
94720               this._maxx = env._maxx;
94721               this._miny = env._miny;
94722               this._maxy = env._maxy;
94723             }
94724           } else if (arguments.length === 2) {
94725             var p1 = arguments[0];
94726             var p2 = arguments[1];
94727             this.init(p1.x, p2.x, p1.y, p2.y);
94728           } else if (arguments.length === 4) {
94729             var x1 = arguments[0];
94730             var x2 = arguments[1];
94731             var y1 = arguments[2];
94732             var y2 = arguments[3];
94733             if (x1 < x2) {
94734               this._minx = x1;
94735               this._maxx = x2;
94736             } else {
94737               this._minx = x2;
94738               this._maxx = x1;
94739             }
94740             if (y1 < y2) {
94741               this._miny = y1;
94742               this._maxy = y2;
94743             } else {
94744               this._miny = y2;
94745               this._maxy = y1;
94746             }
94747           }
94748         };
94749         Envelope.prototype.getMaxY = function getMaxY () {
94750           return this._maxy
94751         };
94752         Envelope.prototype.distance = function distance (env) {
94753           if (this.intersects(env)) { return 0 }
94754           var dx = 0.0;
94755           if (this._maxx < env._minx) { dx = env._minx - this._maxx; } else if (this._minx > env._maxx) { dx = this._minx - env._maxx; }
94756           var dy = 0.0;
94757           if (this._maxy < env._miny) { dy = env._miny - this._maxy; } else if (this._miny > env._maxy) { dy = this._miny - env._maxy; }
94758           if (dx === 0.0) { return dy }
94759           if (dy === 0.0) { return dx }
94760           return Math.sqrt(dx * dx + dy * dy)
94761         };
94762         Envelope.prototype.hashCode = function hashCode () {
94763           var result = 17;
94764           result = 37 * result + Coordinate.hashCode(this._minx);
94765           result = 37 * result + Coordinate.hashCode(this._maxx);
94766           result = 37 * result + Coordinate.hashCode(this._miny);
94767           result = 37 * result + Coordinate.hashCode(this._maxy);
94768           return result
94769         };
94770         Envelope.prototype.interfaces_ = function interfaces_ () {
94771           return [Comparable, Serializable]
94772         };
94773         Envelope.prototype.getClass = function getClass () {
94774           return Envelope
94775         };
94776         Envelope.intersects = function intersects () {
94777           if (arguments.length === 3) {
94778             var p1 = arguments[0];
94779             var p2 = arguments[1];
94780             var q = arguments[2];
94781             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))) {
94782               return true
94783             }
94784             return false
94785           } else if (arguments.length === 4) {
94786             var p1$1 = arguments[0];
94787             var p2$1 = arguments[1];
94788             var q1 = arguments[2];
94789             var q2 = arguments[3];
94790             var minq = Math.min(q1.x, q2.x);
94791             var maxq = Math.max(q1.x, q2.x);
94792             var minp = Math.min(p1$1.x, p2$1.x);
94793             var maxp = Math.max(p1$1.x, p2$1.x);
94794             if (minp > maxq) { return false }
94795             if (maxp < minq) { return false }
94796             minq = Math.min(q1.y, q2.y);
94797             maxq = Math.max(q1.y, q2.y);
94798             minp = Math.min(p1$1.y, p2$1.y);
94799             maxp = Math.max(p1$1.y, p2$1.y);
94800             if (minp > maxq) { return false }
94801             if (maxp < minq) { return false }
94802             return true
94803           }
94804         };
94805         staticAccessors$9.serialVersionUID.get = function () { return 5873921885273102420 };
94806
94807         Object.defineProperties( Envelope, staticAccessors$9 );
94808
94809         var regExes = {
94810           'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
94811           'emptyTypeStr': /^\s*(\w+)\s*EMPTY\s*$/,
94812           'spaces': /\s+/,
94813           'parenComma': /\)\s*,\s*\(/,
94814           'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here
94815           'trimParens': /^\s*\(?(.*?)\)?\s*$/
94816         };
94817
94818         /**
94819          * Class for reading and writing Well-Known Text.
94820          *
94821          * NOTE: Adapted from OpenLayers 2.11 implementation.
94822          */
94823
94824         /** Create a new parser for WKT
94825          *
94826          * @param {GeometryFactory} geometryFactory
94827          * @return An instance of WKTParser.
94828          * @constructor
94829          * @private
94830          */
94831         var WKTParser = function WKTParser (geometryFactory) {
94832           this.geometryFactory = geometryFactory || new GeometryFactory();
94833         };
94834         /**
94835          * Deserialize a WKT string and return a geometry. Supports WKT for POINT,
94836          * MULTIPOINT, LINESTRING, LINEARRING, MULTILINESTRING, POLYGON, MULTIPOLYGON,
94837          * and GEOMETRYCOLLECTION.
94838          *
94839          * @param {String} wkt A WKT string.
94840          * @return {Geometry} A geometry instance.
94841          * @private
94842          */
94843         WKTParser.prototype.read = function read (wkt) {
94844           var geometry, type, str;
94845           wkt = wkt.replace(/[\n\r]/g, ' ');
94846           var matches = regExes.typeStr.exec(wkt);
94847           if (wkt.search('EMPTY') !== -1) {
94848             matches = regExes.emptyTypeStr.exec(wkt);
94849             matches[2] = undefined;
94850           }
94851           if (matches) {
94852             type = matches[1].toLowerCase();
94853             str = matches[2];
94854             if (parse$1[type]) {
94855               geometry = parse$1[type].apply(this, [str]);
94856             }
94857           }
94858
94859           if (geometry === undefined) { throw new Error('Could not parse WKT ' + wkt) }
94860
94861           return geometry
94862         };
94863
94864         /**
94865          * Serialize a geometry into a WKT string.
94866          *
94867          * @param {Geometry} geometry A feature or array of features.
94868          * @return {String} The WKT string representation of the input geometries.
94869          * @private
94870          */
94871         WKTParser.prototype.write = function write (geometry) {
94872           return this.extractGeometry(geometry)
94873         };
94874
94875         /**
94876          * Entry point to construct the WKT for a single Geometry object.
94877          *
94878          * @param {Geometry} geometry
94879          * @return {String} A WKT string of representing the geometry.
94880          * @private
94881          */
94882         WKTParser.prototype.extractGeometry = function extractGeometry (geometry) {
94883           var type = geometry.getGeometryType().toLowerCase();
94884           if (!extract$1[type]) {
94885             return null
94886           }
94887           var wktType = type.toUpperCase();
94888           var data;
94889           if (geometry.isEmpty()) {
94890             data = wktType + ' EMPTY';
94891           } else {
94892             data = wktType + '(' + extract$1[type].apply(this, [geometry]) + ')';
94893           }
94894           return data
94895         };
94896
94897         /**
94898          * Object with properties corresponding to the geometry types. Property values
94899          * are functions that do the actual data extraction.
94900          * @private
94901          */
94902         var extract$1 = {
94903           coordinate: function coordinate (coordinate$1) {
94904             return coordinate$1.x + ' ' + coordinate$1.y
94905           },
94906
94907           /**
94908            * Return a space delimited string of point coordinates.
94909            *
94910            * @param {Point}
94911            *          point
94912            * @return {String} A string of coordinates representing the point.
94913            */
94914           point: function point (point$1) {
94915             return extract$1.coordinate.call(this, point$1._coordinates._coordinates[0])
94916           },
94917
94918           /**
94919            * Return a comma delimited string of point coordinates from a multipoint.
94920            *
94921            * @param {MultiPoint}
94922            *          multipoint
94923            * @return {String} A string of point coordinate strings representing the
94924            *         multipoint.
94925            */
94926           multipoint: function multipoint (multipoint$1) {
94927             var this$1 = this;
94928
94929             var array = [];
94930             for (var i = 0, len = multipoint$1._geometries.length; i < len; ++i) {
94931               array.push('(' + extract$1.point.apply(this$1, [multipoint$1._geometries[i]]) + ')');
94932             }
94933             return array.join(',')
94934           },
94935
94936           /**
94937            * Return a comma delimited string of point coordinates from a line.
94938            *
94939            * @param {LineString} linestring
94940            * @return {String} A string of point coordinate strings representing the linestring.
94941            */
94942           linestring: function linestring (linestring$1) {
94943             var this$1 = this;
94944
94945             var array = [];
94946             for (var i = 0, len = linestring$1._points._coordinates.length; i < len; ++i) {
94947               array.push(extract$1.coordinate.apply(this$1, [linestring$1._points._coordinates[i]]));
94948             }
94949             return array.join(',')
94950           },
94951
94952           linearring: function linearring (linearring$1) {
94953             var this$1 = this;
94954
94955             var array = [];
94956             for (var i = 0, len = linearring$1._points._coordinates.length; i < len; ++i) {
94957               array.push(extract$1.coordinate.apply(this$1, [linearring$1._points._coordinates[i]]));
94958             }
94959             return array.join(',')
94960           },
94961
94962           /**
94963            * Return a comma delimited string of linestring strings from a
94964            * multilinestring.
94965            *
94966            * @param {MultiLineString} multilinestring
94967            * @return {String} A string of of linestring strings representing the multilinestring.
94968            */
94969           multilinestring: function multilinestring (multilinestring$1) {
94970             var this$1 = this;
94971
94972             var array = [];
94973             for (var i = 0, len = multilinestring$1._geometries.length; i < len; ++i) {
94974               array.push('(' +
94975                 extract$1.linestring.apply(this$1, [multilinestring$1._geometries[i]]) +
94976                 ')');
94977             }
94978             return array.join(',')
94979           },
94980
94981           /**
94982            * Return a comma delimited string of linear ring arrays from a polygon.
94983            *
94984            * @param {Polygon} polygon
94985            * @return {String} An array of linear ring arrays representing the polygon.
94986            */
94987           polygon: function polygon (polygon$1) {
94988             var this$1 = this;
94989
94990             var array = [];
94991             array.push('(' + extract$1.linestring.apply(this, [polygon$1._shell]) + ')');
94992             for (var i = 0, len = polygon$1._holes.length; i < len; ++i) {
94993               array.push('(' + extract$1.linestring.apply(this$1, [polygon$1._holes[i]]) + ')');
94994             }
94995             return array.join(',')
94996           },
94997
94998           /**
94999            * Return an array of polygon arrays from a multipolygon.
95000            *
95001            * @param {MultiPolygon} multipolygon
95002            * @return {String} An array of polygon arrays representing the multipolygon.
95003            */
95004           multipolygon: function multipolygon (multipolygon$1) {
95005             var this$1 = this;
95006
95007             var array = [];
95008             for (var i = 0, len = multipolygon$1._geometries.length; i < len; ++i) {
95009               array.push('(' + extract$1.polygon.apply(this$1, [multipolygon$1._geometries[i]]) + ')');
95010             }
95011             return array.join(',')
95012           },
95013
95014           /**
95015            * Return the WKT portion between 'GEOMETRYCOLLECTION(' and ')' for an
95016            * geometrycollection.
95017            *
95018            * @param {GeometryCollection} collection
95019            * @return {String} internal WKT representation of the collection.
95020            */
95021           geometrycollection: function geometrycollection (collection) {
95022             var this$1 = this;
95023
95024             var array = [];
95025             for (var i = 0, len = collection._geometries.length; i < len; ++i) {
95026               array.push(this$1.extractGeometry(collection._geometries[i]));
95027             }
95028             return array.join(',')
95029           }
95030         };
95031
95032         /**
95033          * Object with properties corresponding to the geometry types. Property values
95034          * are functions that do the actual parsing.
95035          * @private
95036          */
95037         var parse$1 = {
95038           /**
95039            * Return point geometry given a point WKT fragment.
95040            *
95041            * @param {String} str A WKT fragment representing the point.
95042            * @return {Point} A point geometry.
95043            * @private
95044            */
95045           point: function point (str) {
95046             if (str === undefined) {
95047               return this.geometryFactory.createPoint()
95048             }
95049
95050             var coords = str.trim().split(regExes.spaces);
95051             return this.geometryFactory.createPoint(new Coordinate(Number.parseFloat(coords[0]),
95052               Number.parseFloat(coords[1])))
95053           },
95054
95055           /**
95056            * Return a multipoint geometry given a multipoint WKT fragment.
95057            *
95058            * @param {String} str A WKT fragment representing the multipoint.
95059            * @return {Point} A multipoint feature.
95060            * @private
95061            */
95062           multipoint: function multipoint (str) {
95063             var this$1 = this;
95064
95065             if (str === undefined) {
95066               return this.geometryFactory.createMultiPoint()
95067             }
95068
95069             var point;
95070             var points = str.trim().split(',');
95071             var components = [];
95072             for (var i = 0, len = points.length; i < len; ++i) {
95073               point = points[i].replace(regExes.trimParens, '$1');
95074               components.push(parse$1.point.apply(this$1, [point]));
95075             }
95076             return this.geometryFactory.createMultiPoint(components)
95077           },
95078
95079           /**
95080            * Return a linestring geometry given a linestring WKT fragment.
95081            *
95082            * @param {String} str A WKT fragment representing the linestring.
95083            * @return {LineString} A linestring geometry.
95084            * @private
95085            */
95086           linestring: function linestring (str) {
95087             if (str === undefined) {
95088               return this.geometryFactory.createLineString()
95089             }
95090
95091             var points = str.trim().split(',');
95092             var components = [];
95093             var coords;
95094             for (var i = 0, len = points.length; i < len; ++i) {
95095               coords = points[i].trim().split(regExes.spaces);
95096               components.push(new Coordinate(Number.parseFloat(coords[0]), Number.parseFloat(coords[1])));
95097             }
95098             return this.geometryFactory.createLineString(components)
95099           },
95100
95101           /**
95102            * Return a linearring geometry given a linearring WKT fragment.
95103            *
95104            * @param {String} str A WKT fragment representing the linearring.
95105            * @return {LinearRing} A linearring geometry.
95106            * @private
95107            */
95108           linearring: function linearring (str) {
95109             if (str === undefined) {
95110               return this.geometryFactory.createLinearRing()
95111             }
95112
95113             var points = str.trim().split(',');
95114             var components = [];
95115             var coords;
95116             for (var i = 0, len = points.length; i < len; ++i) {
95117               coords = points[i].trim().split(regExes.spaces);
95118               components.push(new Coordinate(Number.parseFloat(coords[0]), Number.parseFloat(coords[1])));
95119             }
95120             return this.geometryFactory.createLinearRing(components)
95121           },
95122
95123           /**
95124            * Return a multilinestring geometry given a multilinestring WKT fragment.
95125            *
95126            * @param {String} str A WKT fragment representing the multilinestring.
95127            * @return {MultiLineString} A multilinestring geometry.
95128            * @private
95129            */
95130           multilinestring: function multilinestring (str) {
95131             var this$1 = this;
95132
95133             if (str === undefined) {
95134               return this.geometryFactory.createMultiLineString()
95135             }
95136
95137             var line;
95138             var lines = str.trim().split(regExes.parenComma);
95139             var components = [];
95140             for (var i = 0, len = lines.length; i < len; ++i) {
95141               line = lines[i].replace(regExes.trimParens, '$1');
95142               components.push(parse$1.linestring.apply(this$1, [line]));
95143             }
95144             return this.geometryFactory.createMultiLineString(components)
95145           },
95146
95147           /**
95148            * Return a polygon geometry given a polygon WKT fragment.
95149            *
95150            * @param {String} str A WKT fragment representing the polygon.
95151            * @return {Polygon} A polygon geometry.
95152            * @private
95153            */
95154           polygon: function polygon (str) {
95155             var this$1 = this;
95156
95157             if (str === undefined) {
95158               return this.geometryFactory.createPolygon()
95159             }
95160
95161             var ring, linestring, linearring;
95162             var rings = str.trim().split(regExes.parenComma);
95163             var shell;
95164             var holes = [];
95165             for (var i = 0, len = rings.length; i < len; ++i) {
95166               ring = rings[i].replace(regExes.trimParens, '$1');
95167               linestring = parse$1.linestring.apply(this$1, [ring]);
95168               linearring = this$1.geometryFactory.createLinearRing(linestring._points);
95169               if (i === 0) {
95170                 shell = linearring;
95171               } else {
95172                 holes.push(linearring);
95173               }
95174             }
95175             return this.geometryFactory.createPolygon(shell, holes)
95176           },
95177
95178           /**
95179            * Return a multipolygon geometry given a multipolygon WKT fragment.
95180            *
95181            * @param {String} str A WKT fragment representing the multipolygon.
95182            * @return {MultiPolygon} A multipolygon geometry.
95183            * @private
95184            */
95185           multipolygon: function multipolygon (str) {
95186             var this$1 = this;
95187
95188             if (str === undefined) {
95189               return this.geometryFactory.createMultiPolygon()
95190             }
95191
95192             var polygon;
95193             var polygons = str.trim().split(regExes.doubleParenComma);
95194             var components = [];
95195             for (var i = 0, len = polygons.length; i < len; ++i) {
95196               polygon = polygons[i].replace(regExes.trimParens, '$1');
95197               components.push(parse$1.polygon.apply(this$1, [polygon]));
95198             }
95199             return this.geometryFactory.createMultiPolygon(components)
95200           },
95201
95202           /**
95203            * Return a geometrycollection given a geometrycollection WKT fragment.
95204            *
95205            * @param {String} str A WKT fragment representing the geometrycollection.
95206            * @return {GeometryCollection}
95207            * @private
95208            */
95209           geometrycollection: function geometrycollection (str) {
95210             var this$1 = this;
95211
95212             if (str === undefined) {
95213               return this.geometryFactory.createGeometryCollection()
95214             }
95215
95216             // separate components of the collection with |
95217             str = str.replace(/,\s*([A-Za-z])/g, '|$1');
95218             var wktArray = str.trim().split('|');
95219             var components = [];
95220             for (var i = 0, len = wktArray.length; i < len; ++i) {
95221               components.push(this$1.read(wktArray[i]));
95222             }
95223             return this.geometryFactory.createGeometryCollection(components)
95224           }
95225         };
95226
95227         /**
95228          * Writes the Well-Known Text representation of a {@link Geometry}. The
95229          * Well-Known Text format is defined in the <A
95230          * HREF="http://www.opengis.org/techno/specs.htm"> OGC Simple Features
95231          * Specification for SQL</A>.
95232          * <p>
95233          * The <code>WKTWriter</code> outputs coordinates rounded to the precision
95234          * model. Only the maximum number of decimal places necessary to represent the
95235          * ordinates to the required precision will be output.
95236          * <p>
95237          * The SFS WKT spec does not define a special tag for {@link LinearRing}s.
95238          * Under the spec, rings are output as <code>LINESTRING</code>s.
95239          */
95240
95241         /**
95242          * @param {GeometryFactory} geometryFactory
95243          * @constructor
95244          */
95245         var WKTWriter = function WKTWriter (geometryFactory) {
95246           this.parser = new WKTParser(geometryFactory);
95247         };
95248
95249         /**
95250          * Converts a <code>Geometry</code> to its Well-known Text representation.
95251          *
95252          * @param {Geometry} geometry a <code>Geometry</code> to process.
95253          * @return {string} a <Geometry Tagged Text> string (see the OpenGIS Simple
95254          *       Features Specification).
95255          * @memberof WKTWriter
95256          */
95257         WKTWriter.prototype.write = function write (geometry) {
95258           return this.parser.write(geometry)
95259         };
95260         /**
95261          * Generates the WKT for a <tt>LINESTRING</tt> specified by two
95262          * {@link Coordinate}s.
95263          *
95264          * @param p0 the first coordinate.
95265          * @param p1 the second coordinate.
95266          *
95267          * @return the WKT.
95268          * @private
95269          */
95270         WKTWriter.toLineString = function toLineString (p0, p1) {
95271           if (arguments.length !== 2) {
95272             throw new Error('Not implemented')
95273           }
95274           return 'LINESTRING ( ' + p0.x + ' ' + p0.y + ', ' + p1.x + ' ' + p1.y + ' )'
95275         };
95276
95277         var RuntimeException = (function (Error) {
95278           function RuntimeException (message) {
95279             Error.call(this, message);
95280             this.name = 'RuntimeException';
95281             this.message = message;
95282             this.stack = (new Error()).stack;
95283           }
95284
95285           if ( Error ) RuntimeException.__proto__ = Error;
95286           RuntimeException.prototype = Object.create( Error && Error.prototype );
95287           RuntimeException.prototype.constructor = RuntimeException;
95288
95289           return RuntimeException;
95290         }(Error));
95291
95292         var AssertionFailedException = (function (RuntimeException$$1) {
95293           function AssertionFailedException () {
95294             RuntimeException$$1.call(this);
95295             if (arguments.length === 0) {
95296               RuntimeException$$1.call(this);
95297             } else if (arguments.length === 1) {
95298               var message = arguments[0];
95299               RuntimeException$$1.call(this, message);
95300             }
95301           }
95302
95303           if ( RuntimeException$$1 ) AssertionFailedException.__proto__ = RuntimeException$$1;
95304           AssertionFailedException.prototype = Object.create( RuntimeException$$1 && RuntimeException$$1.prototype );
95305           AssertionFailedException.prototype.constructor = AssertionFailedException;
95306           AssertionFailedException.prototype.interfaces_ = function interfaces_ () {
95307             return []
95308           };
95309           AssertionFailedException.prototype.getClass = function getClass () {
95310             return AssertionFailedException
95311           };
95312
95313           return AssertionFailedException;
95314         }(RuntimeException));
95315
95316         var Assert = function Assert () {};
95317
95318         Assert.prototype.interfaces_ = function interfaces_ () {
95319           return []
95320         };
95321         Assert.prototype.getClass = function getClass () {
95322           return Assert
95323         };
95324         Assert.shouldNeverReachHere = function shouldNeverReachHere () {
95325           if (arguments.length === 0) {
95326             Assert.shouldNeverReachHere(null);
95327           } else if (arguments.length === 1) {
95328             var message = arguments[0];
95329             throw new AssertionFailedException('Should never reach here' + (message !== null ? ': ' + message : ''))
95330           }
95331         };
95332         Assert.isTrue = function isTrue () {
95333           var assertion;
95334           var message;
95335           if (arguments.length === 1) {
95336             assertion = arguments[0];
95337             Assert.isTrue(assertion, null);
95338           } else if (arguments.length === 2) {
95339             assertion = arguments[0];
95340             message = arguments[1];
95341             if (!assertion) {
95342               if (message === null) {
95343                 throw new AssertionFailedException()
95344               } else {
95345                 throw new AssertionFailedException(message)
95346               }
95347             }
95348           }
95349         };
95350         Assert.equals = function equals () {
95351           var expectedValue;
95352           var actualValue;
95353           var message;
95354           if (arguments.length === 2) {
95355             expectedValue = arguments[0];
95356             actualValue = arguments[1];
95357             Assert.equals(expectedValue, actualValue, null);
95358           } else if (arguments.length === 3) {
95359             expectedValue = arguments[0];
95360             actualValue = arguments[1];
95361             message = arguments[2];
95362             if (!actualValue.equals(expectedValue)) {
95363               throw new AssertionFailedException('Expected ' + expectedValue + ' but encountered ' + actualValue + (message !== null ? ': ' + message : ''))
95364             }
95365           }
95366         };
95367
95368         var LineIntersector = function LineIntersector () {
95369           this._result = null;
95370           this._inputLines = Array(2).fill().map(function () { return Array(2); });
95371           this._intPt = new Array(2).fill(null);
95372           this._intLineIndex = null;
95373           this._isProper = null;
95374           this._pa = null;
95375           this._pb = null;
95376           this._precisionModel = null;
95377           this._intPt[0] = new Coordinate();
95378           this._intPt[1] = new Coordinate();
95379           this._pa = this._intPt[0];
95380           this._pb = this._intPt[1];
95381           this._result = 0;
95382         };
95383
95384         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 } };
95385         LineIntersector.prototype.getIndexAlongSegment = function getIndexAlongSegment (segmentIndex, intIndex) {
95386           this.computeIntLineIndex();
95387           return this._intLineIndex[segmentIndex][intIndex]
95388         };
95389         LineIntersector.prototype.getTopologySummary = function getTopologySummary () {
95390           var catBuf = new StringBuffer();
95391           if (this.isEndPoint()) { catBuf.append(' endpoint'); }
95392           if (this._isProper) { catBuf.append(' proper'); }
95393           if (this.isCollinear()) { catBuf.append(' collinear'); }
95394           return catBuf.toString()
95395         };
95396         LineIntersector.prototype.computeIntersection = function computeIntersection (p1, p2, p3, p4) {
95397           this._inputLines[0][0] = p1;
95398           this._inputLines[0][1] = p2;
95399           this._inputLines[1][0] = p3;
95400           this._inputLines[1][1] = p4;
95401           this._result = this.computeIntersect(p1, p2, p3, p4);
95402         };
95403         LineIntersector.prototype.getIntersectionNum = function getIntersectionNum () {
95404           return this._result
95405         };
95406         LineIntersector.prototype.computeIntLineIndex = function computeIntLineIndex () {
95407           if (arguments.length === 0) {
95408             if (this._intLineIndex === null) {
95409               this._intLineIndex = Array(2).fill().map(function () { return Array(2); });
95410               this.computeIntLineIndex(0);
95411               this.computeIntLineIndex(1);
95412             }
95413           } else if (arguments.length === 1) {
95414             var segmentIndex = arguments[0];
95415             var dist0 = this.getEdgeDistance(segmentIndex, 0);
95416             var dist1 = this.getEdgeDistance(segmentIndex, 1);
95417             if (dist0 > dist1) {
95418               this._intLineIndex[segmentIndex][0] = 0;
95419               this._intLineIndex[segmentIndex][1] = 1;
95420             } else {
95421               this._intLineIndex[segmentIndex][0] = 1;
95422               this._intLineIndex[segmentIndex][1] = 0;
95423             }
95424           }
95425         };
95426         LineIntersector.prototype.isProper = function isProper () {
95427           return this.hasIntersection() && this._isProper
95428         };
95429         LineIntersector.prototype.setPrecisionModel = function setPrecisionModel (precisionModel) {
95430           this._precisionModel = precisionModel;
95431         };
95432         LineIntersector.prototype.isInteriorIntersection = function isInteriorIntersection () {
95433             var this$1 = this;
95434
95435           if (arguments.length === 0) {
95436             if (this.isInteriorIntersection(0)) { return true }
95437             if (this.isInteriorIntersection(1)) { return true }
95438             return false
95439           } else if (arguments.length === 1) {
95440             var inputLineIndex = arguments[0];
95441             for (var i = 0; i < this._result; i++) {
95442               if (!(this$1._intPt[i].equals2D(this$1._inputLines[inputLineIndex][0]) || this$1._intPt[i].equals2D(this$1._inputLines[inputLineIndex][1]))) {
95443                 return true
95444               }
95445             }
95446             return false
95447           }
95448         };
95449         LineIntersector.prototype.getIntersection = function getIntersection (intIndex) {
95450           return this._intPt[intIndex]
95451         };
95452         LineIntersector.prototype.isEndPoint = function isEndPoint () {
95453           return this.hasIntersection() && !this._isProper
95454         };
95455         LineIntersector.prototype.hasIntersection = function hasIntersection () {
95456           return this._result !== LineIntersector.NO_INTERSECTION
95457         };
95458         LineIntersector.prototype.getEdgeDistance = function getEdgeDistance (segmentIndex, intIndex) {
95459           var dist = LineIntersector.computeEdgeDistance(this._intPt[intIndex], this._inputLines[segmentIndex][0], this._inputLines[segmentIndex][1]);
95460           return dist
95461         };
95462         LineIntersector.prototype.isCollinear = function isCollinear () {
95463           return this._result === LineIntersector.COLLINEAR_INTERSECTION
95464         };
95465         LineIntersector.prototype.toString = function toString () {
95466           return WKTWriter.toLineString(this._inputLines[0][0], this._inputLines[0][1]) + ' - ' + WKTWriter.toLineString(this._inputLines[1][0], this._inputLines[1][1]) + this.getTopologySummary()
95467         };
95468         LineIntersector.prototype.getEndpoint = function getEndpoint (segmentIndex, ptIndex) {
95469           return this._inputLines[segmentIndex][ptIndex]
95470         };
95471         LineIntersector.prototype.isIntersection = function isIntersection (pt) {
95472             var this$1 = this;
95473
95474           for (var i = 0; i < this._result; i++) {
95475             if (this$1._intPt[i].equals2D(pt)) {
95476               return true
95477             }
95478           }
95479           return false
95480         };
95481         LineIntersector.prototype.getIntersectionAlongSegment = function getIntersectionAlongSegment (segmentIndex, intIndex) {
95482           this.computeIntLineIndex();
95483           return this._intPt[this._intLineIndex[segmentIndex][intIndex]]
95484         };
95485         LineIntersector.prototype.interfaces_ = function interfaces_ () {
95486           return []
95487         };
95488         LineIntersector.prototype.getClass = function getClass () {
95489           return LineIntersector
95490         };
95491         LineIntersector.computeEdgeDistance = function computeEdgeDistance (p, p0, p1) {
95492           var dx = Math.abs(p1.x - p0.x);
95493           var dy = Math.abs(p1.y - p0.y);
95494           var dist = -1.0;
95495           if (p.equals(p0)) {
95496             dist = 0.0;
95497           } else if (p.equals(p1)) {
95498             if (dx > dy) { dist = dx; } else { dist = dy; }
95499           } else {
95500             var pdx = Math.abs(p.x - p0.x);
95501             var pdy = Math.abs(p.y - p0.y);
95502             if (dx > dy) { dist = pdx; } else { dist = pdy; }
95503             if (dist === 0.0 && !p.equals(p0)) {
95504               dist = Math.max(pdx, pdy);
95505             }
95506           }
95507           Assert.isTrue(!(dist === 0.0 && !p.equals(p0)), 'Bad distance calculation');
95508           return dist
95509         };
95510         LineIntersector.nonRobustComputeEdgeDistance = function nonRobustComputeEdgeDistance (p, p1, p2) {
95511           var dx = p.x - p1.x;
95512           var dy = p.y - p1.y;
95513           var dist = Math.sqrt(dx * dx + dy * dy);
95514           Assert.isTrue(!(dist === 0.0 && !p.equals(p1)), 'Invalid distance calculation');
95515           return dist
95516         };
95517         staticAccessors$10.DONT_INTERSECT.get = function () { return 0 };
95518         staticAccessors$10.DO_INTERSECT.get = function () { return 1 };
95519         staticAccessors$10.COLLINEAR.get = function () { return 2 };
95520         staticAccessors$10.NO_INTERSECTION.get = function () { return 0 };
95521         staticAccessors$10.POINT_INTERSECTION.get = function () { return 1 };
95522         staticAccessors$10.COLLINEAR_INTERSECTION.get = function () { return 2 };
95523
95524         Object.defineProperties( LineIntersector, staticAccessors$10 );
95525
95526         var RobustLineIntersector = (function (LineIntersector$$1) {
95527           function RobustLineIntersector () {
95528             LineIntersector$$1.apply(this, arguments);
95529           }
95530
95531           if ( LineIntersector$$1 ) RobustLineIntersector.__proto__ = LineIntersector$$1;
95532           RobustLineIntersector.prototype = Object.create( LineIntersector$$1 && LineIntersector$$1.prototype );
95533           RobustLineIntersector.prototype.constructor = RobustLineIntersector;
95534
95535           RobustLineIntersector.prototype.isInSegmentEnvelopes = function isInSegmentEnvelopes (intPt) {
95536             var env0 = new Envelope(this._inputLines[0][0], this._inputLines[0][1]);
95537             var env1 = new Envelope(this._inputLines[1][0], this._inputLines[1][1]);
95538             return env0.contains(intPt) && env1.contains(intPt)
95539           };
95540           RobustLineIntersector.prototype.computeIntersection = function computeIntersection () {
95541             if (arguments.length === 3) {
95542               var p = arguments[0];
95543               var p1 = arguments[1];
95544               var p2 = arguments[2];
95545               this._isProper = false;
95546               if (Envelope.intersects(p1, p2, p)) {
95547                 if (CGAlgorithms.orientationIndex(p1, p2, p) === 0 && CGAlgorithms.orientationIndex(p2, p1, p) === 0) {
95548                   this._isProper = true;
95549                   if (p.equals(p1) || p.equals(p2)) {
95550                     this._isProper = false;
95551                   }
95552                   this._result = LineIntersector$$1.POINT_INTERSECTION;
95553                   return null
95554                 }
95555               }
95556               this._result = LineIntersector$$1.NO_INTERSECTION;
95557             } else { return LineIntersector$$1.prototype.computeIntersection.apply(this, arguments) }
95558           };
95559           RobustLineIntersector.prototype.normalizeToMinimum = function normalizeToMinimum (n1, n2, n3, n4, normPt) {
95560             normPt.x = this.smallestInAbsValue(n1.x, n2.x, n3.x, n4.x);
95561             normPt.y = this.smallestInAbsValue(n1.y, n2.y, n3.y, n4.y);
95562             n1.x -= normPt.x;
95563             n1.y -= normPt.y;
95564             n2.x -= normPt.x;
95565             n2.y -= normPt.y;
95566             n3.x -= normPt.x;
95567             n3.y -= normPt.y;
95568             n4.x -= normPt.x;
95569             n4.y -= normPt.y;
95570           };
95571           RobustLineIntersector.prototype.safeHCoordinateIntersection = function safeHCoordinateIntersection (p1, p2, q1, q2) {
95572             var intPt = null;
95573             try {
95574               intPt = HCoordinate.intersection(p1, p2, q1, q2);
95575             } catch (e) {
95576               if (e instanceof NotRepresentableException) {
95577                 intPt = RobustLineIntersector.nearestEndpoint(p1, p2, q1, q2);
95578               } else { throw e }
95579             } finally {}
95580             return intPt
95581           };
95582           RobustLineIntersector.prototype.intersection = function intersection (p1, p2, q1, q2) {
95583             var intPt = this.intersectionWithNormalization(p1, p2, q1, q2);
95584             if (!this.isInSegmentEnvelopes(intPt)) {
95585               intPt = new Coordinate(RobustLineIntersector.nearestEndpoint(p1, p2, q1, q2));
95586             }
95587             if (this._precisionModel !== null) {
95588               this._precisionModel.makePrecise(intPt);
95589             }
95590             return intPt
95591           };
95592           RobustLineIntersector.prototype.smallestInAbsValue = function smallestInAbsValue (x1, x2, x3, x4) {
95593             var x = x1;
95594             var xabs = Math.abs(x);
95595             if (Math.abs(x2) < xabs) {
95596               x = x2;
95597               xabs = Math.abs(x2);
95598             }
95599             if (Math.abs(x3) < xabs) {
95600               x = x3;
95601               xabs = Math.abs(x3);
95602             }
95603             if (Math.abs(x4) < xabs) {
95604               x = x4;
95605             }
95606             return x
95607           };
95608           RobustLineIntersector.prototype.checkDD = function checkDD (p1, p2, q1, q2, intPt) {
95609             var intPtDD = CGAlgorithmsDD.intersection(p1, p2, q1, q2);
95610             var isIn = this.isInSegmentEnvelopes(intPtDD);
95611             System.out.println('DD in env = ' + isIn + '  --------------------- ' + intPtDD);
95612             if (intPt.distance(intPtDD) > 0.0001) {
95613               System.out.println('Distance = ' + intPt.distance(intPtDD));
95614             }
95615           };
95616           RobustLineIntersector.prototype.intersectionWithNormalization = function intersectionWithNormalization (p1, p2, q1, q2) {
95617             var n1 = new Coordinate(p1);
95618             var n2 = new Coordinate(p2);
95619             var n3 = new Coordinate(q1);
95620             var n4 = new Coordinate(q2);
95621             var normPt = new Coordinate();
95622             this.normalizeToEnvCentre(n1, n2, n3, n4, normPt);
95623             var intPt = this.safeHCoordinateIntersection(n1, n2, n3, n4);
95624             intPt.x += normPt.x;
95625             intPt.y += normPt.y;
95626             return intPt
95627           };
95628           RobustLineIntersector.prototype.computeCollinearIntersection = function computeCollinearIntersection (p1, p2, q1, q2) {
95629             var p1q1p2 = Envelope.intersects(p1, p2, q1);
95630             var p1q2p2 = Envelope.intersects(p1, p2, q2);
95631             var q1p1q2 = Envelope.intersects(q1, q2, p1);
95632             var q1p2q2 = Envelope.intersects(q1, q2, p2);
95633             if (p1q1p2 && p1q2p2) {
95634               this._intPt[0] = q1;
95635               this._intPt[1] = q2;
95636               return LineIntersector$$1.COLLINEAR_INTERSECTION
95637             }
95638             if (q1p1q2 && q1p2q2) {
95639               this._intPt[0] = p1;
95640               this._intPt[1] = p2;
95641               return LineIntersector$$1.COLLINEAR_INTERSECTION
95642             }
95643             if (p1q1p2 && q1p1q2) {
95644               this._intPt[0] = q1;
95645               this._intPt[1] = p1;
95646               return q1.equals(p1) && !p1q2p2 && !q1p2q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95647             }
95648             if (p1q1p2 && q1p2q2) {
95649               this._intPt[0] = q1;
95650               this._intPt[1] = p2;
95651               return q1.equals(p2) && !p1q2p2 && !q1p1q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95652             }
95653             if (p1q2p2 && q1p1q2) {
95654               this._intPt[0] = q2;
95655               this._intPt[1] = p1;
95656               return q2.equals(p1) && !p1q1p2 && !q1p2q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95657             }
95658             if (p1q2p2 && q1p2q2) {
95659               this._intPt[0] = q2;
95660               this._intPt[1] = p2;
95661               return q2.equals(p2) && !p1q1p2 && !q1p1q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95662             }
95663             return LineIntersector$$1.NO_INTERSECTION
95664           };
95665           RobustLineIntersector.prototype.normalizeToEnvCentre = function normalizeToEnvCentre (n00, n01, n10, n11, normPt) {
95666             var minX0 = n00.x < n01.x ? n00.x : n01.x;
95667             var minY0 = n00.y < n01.y ? n00.y : n01.y;
95668             var maxX0 = n00.x > n01.x ? n00.x : n01.x;
95669             var maxY0 = n00.y > n01.y ? n00.y : n01.y;
95670             var minX1 = n10.x < n11.x ? n10.x : n11.x;
95671             var minY1 = n10.y < n11.y ? n10.y : n11.y;
95672             var maxX1 = n10.x > n11.x ? n10.x : n11.x;
95673             var maxY1 = n10.y > n11.y ? n10.y : n11.y;
95674             var intMinX = minX0 > minX1 ? minX0 : minX1;
95675             var intMaxX = maxX0 < maxX1 ? maxX0 : maxX1;
95676             var intMinY = minY0 > minY1 ? minY0 : minY1;
95677             var intMaxY = maxY0 < maxY1 ? maxY0 : maxY1;
95678             var intMidX = (intMinX + intMaxX) / 2.0;
95679             var intMidY = (intMinY + intMaxY) / 2.0;
95680             normPt.x = intMidX;
95681             normPt.y = intMidY;
95682             n00.x -= normPt.x;
95683             n00.y -= normPt.y;
95684             n01.x -= normPt.x;
95685             n01.y -= normPt.y;
95686             n10.x -= normPt.x;
95687             n10.y -= normPt.y;
95688             n11.x -= normPt.x;
95689             n11.y -= normPt.y;
95690           };
95691           RobustLineIntersector.prototype.computeIntersect = function computeIntersect (p1, p2, q1, q2) {
95692             this._isProper = false;
95693             if (!Envelope.intersects(p1, p2, q1, q2)) { return LineIntersector$$1.NO_INTERSECTION }
95694             var Pq1 = CGAlgorithms.orientationIndex(p1, p2, q1);
95695             var Pq2 = CGAlgorithms.orientationIndex(p1, p2, q2);
95696             if ((Pq1 > 0 && Pq2 > 0) || (Pq1 < 0 && Pq2 < 0)) {
95697               return LineIntersector$$1.NO_INTERSECTION
95698             }
95699             var Qp1 = CGAlgorithms.orientationIndex(q1, q2, p1);
95700             var Qp2 = CGAlgorithms.orientationIndex(q1, q2, p2);
95701             if ((Qp1 > 0 && Qp2 > 0) || (Qp1 < 0 && Qp2 < 0)) {
95702               return LineIntersector$$1.NO_INTERSECTION
95703             }
95704             var collinear = Pq1 === 0 && Pq2 === 0 && Qp1 === 0 && Qp2 === 0;
95705             if (collinear) {
95706               return this.computeCollinearIntersection(p1, p2, q1, q2)
95707             }
95708             if (Pq1 === 0 || Pq2 === 0 || Qp1 === 0 || Qp2 === 0) {
95709               this._isProper = false;
95710               if (p1.equals2D(q1) || p1.equals2D(q2)) {
95711                 this._intPt[0] = p1;
95712               } else if (p2.equals2D(q1) || p2.equals2D(q2)) {
95713                 this._intPt[0] = p2;
95714               } else if (Pq1 === 0) {
95715                 this._intPt[0] = new Coordinate(q1);
95716               } else if (Pq2 === 0) {
95717                 this._intPt[0] = new Coordinate(q2);
95718               } else if (Qp1 === 0) {
95719                 this._intPt[0] = new Coordinate(p1);
95720               } else if (Qp2 === 0) {
95721                 this._intPt[0] = new Coordinate(p2);
95722               }
95723             } else {
95724               this._isProper = true;
95725               this._intPt[0] = this.intersection(p1, p2, q1, q2);
95726             }
95727             return LineIntersector$$1.POINT_INTERSECTION
95728           };
95729           RobustLineIntersector.prototype.interfaces_ = function interfaces_ () {
95730             return []
95731           };
95732           RobustLineIntersector.prototype.getClass = function getClass () {
95733             return RobustLineIntersector
95734           };
95735           RobustLineIntersector.nearestEndpoint = function nearestEndpoint (p1, p2, q1, q2) {
95736             var nearestPt = p1;
95737             var minDist = CGAlgorithms.distancePointLine(p1, q1, q2);
95738             var dist = CGAlgorithms.distancePointLine(p2, q1, q2);
95739             if (dist < minDist) {
95740               minDist = dist;
95741               nearestPt = p2;
95742             }
95743             dist = CGAlgorithms.distancePointLine(q1, p1, p2);
95744             if (dist < minDist) {
95745               minDist = dist;
95746               nearestPt = q1;
95747             }
95748             dist = CGAlgorithms.distancePointLine(q2, p1, p2);
95749             if (dist < minDist) {
95750               minDist = dist;
95751               nearestPt = q2;
95752             }
95753             return nearestPt
95754           };
95755
95756           return RobustLineIntersector;
95757         }(LineIntersector));
95758
95759         var RobustDeterminant = function RobustDeterminant () {};
95760
95761         RobustDeterminant.prototype.interfaces_ = function interfaces_ () {
95762           return []
95763         };
95764         RobustDeterminant.prototype.getClass = function getClass () {
95765           return RobustDeterminant
95766         };
95767         RobustDeterminant.orientationIndex = function orientationIndex (p1, p2, q) {
95768           var dx1 = p2.x - p1.x;
95769           var dy1 = p2.y - p1.y;
95770           var dx2 = q.x - p2.x;
95771           var dy2 = q.y - p2.y;
95772           return RobustDeterminant.signOfDet2x2(dx1, dy1, dx2, dy2)
95773         };
95774         RobustDeterminant.signOfDet2x2 = function signOfDet2x2 (x1, y1, x2, y2) {
95775           var sign = null;
95776           var swap = null;
95777           var k = null;
95778           sign = 1;
95779           if (x1 === 0.0 || y2 === 0.0) {
95780             if (y1 === 0.0 || x2 === 0.0) {
95781               return 0
95782             } else if (y1 > 0) {
95783               if (x2 > 0) {
95784                 return -sign
95785               } else {
95786                 return sign
95787               }
95788             } else {
95789               if (x2 > 0) {
95790                 return sign
95791               } else {
95792                 return -sign
95793               }
95794             }
95795           }
95796           if (y1 === 0.0 || x2 === 0.0) {
95797             if (y2 > 0) {
95798               if (x1 > 0) {
95799                 return sign
95800               } else {
95801                 return -sign
95802               }
95803             } else {
95804               if (x1 > 0) {
95805                 return -sign
95806               } else {
95807                 return sign
95808               }
95809             }
95810           }
95811           if (y1 > 0.0) {
95812             if (y2 > 0.0) {
95813               if (y1 <= y2) ; else {
95814                 sign = -sign;
95815                 swap = x1;
95816                 x1 = x2;
95817                 x2 = swap;
95818                 swap = y1;
95819                 y1 = y2;
95820                 y2 = swap;
95821               }
95822             } else {
95823               if (y1 <= -y2) {
95824                 sign = -sign;
95825                 x2 = -x2;
95826                 y2 = -y2;
95827               } else {
95828                 swap = x1;
95829                 x1 = -x2;
95830                 x2 = swap;
95831                 swap = y1;
95832                 y1 = -y2;
95833                 y2 = swap;
95834               }
95835             }
95836           } else {
95837             if (y2 > 0.0) {
95838               if (-y1 <= y2) {
95839                 sign = -sign;
95840                 x1 = -x1;
95841                 y1 = -y1;
95842               } else {
95843                 swap = -x1;
95844                 x1 = x2;
95845                 x2 = swap;
95846                 swap = -y1;
95847                 y1 = y2;
95848                 y2 = swap;
95849               }
95850             } else {
95851               if (y1 >= y2) {
95852                 x1 = -x1;
95853                 y1 = -y1;
95854                 x2 = -x2;
95855                 y2 = -y2;
95856               } else {
95857                 sign = -sign;
95858                 swap = -x1;
95859                 x1 = -x2;
95860                 x2 = swap;
95861                 swap = -y1;
95862                 y1 = -y2;
95863                 y2 = swap;
95864               }
95865             }
95866           }
95867           if (x1 > 0.0) {
95868             if (x2 > 0.0) {
95869               if (x1 <= x2) ; else {
95870                 return sign
95871               }
95872             } else {
95873               return sign
95874             }
95875           } else {
95876             if (x2 > 0.0) {
95877               return -sign
95878             } else {
95879               if (x1 >= x2) {
95880                 sign = -sign;
95881                 x1 = -x1;
95882                 x2 = -x2;
95883               } else {
95884                 return -sign
95885               }
95886             }
95887           }
95888           while (true) {
95889             k = Math.floor(x2 / x1);
95890             x2 = x2 - k * x1;
95891             y2 = y2 - k * y1;
95892             if (y2 < 0.0) {
95893               return -sign
95894             }
95895             if (y2 > y1) {
95896               return sign
95897             }
95898             if (x1 > x2 + x2) {
95899               if (y1 < y2 + y2) {
95900                 return sign
95901               }
95902             } else {
95903               if (y1 > y2 + y2) {
95904                 return -sign
95905               } else {
95906                 x2 = x1 - x2;
95907                 y2 = y1 - y2;
95908                 sign = -sign;
95909               }
95910             }
95911             if (y2 === 0.0) {
95912               if (x2 === 0.0) {
95913                 return 0
95914               } else {
95915                 return -sign
95916               }
95917             }
95918             if (x2 === 0.0) {
95919               return sign
95920             }
95921             k = Math.floor(x1 / x2);
95922             x1 = x1 - k * x2;
95923             y1 = y1 - k * y2;
95924             if (y1 < 0.0) {
95925               return sign
95926             }
95927             if (y1 > y2) {
95928               return -sign
95929             }
95930             if (x2 > x1 + x1) {
95931               if (y2 < y1 + y1) {
95932                 return -sign
95933               }
95934             } else {
95935               if (y2 > y1 + y1) {
95936                 return sign
95937               } else {
95938                 x1 = x2 - x1;
95939                 y1 = y2 - y1;
95940                 sign = -sign;
95941               }
95942             }
95943             if (y1 === 0.0) {
95944               if (x1 === 0.0) {
95945                 return 0
95946               } else {
95947                 return sign
95948               }
95949             }
95950             if (x1 === 0.0) {
95951               return -sign
95952             }
95953           }
95954         };
95955
95956         var RayCrossingCounter = function RayCrossingCounter () {
95957           this._p = null;
95958           this._crossingCount = 0;
95959           this._isPointOnSegment = false;
95960           var p = arguments[0];
95961           this._p = p;
95962         };
95963         RayCrossingCounter.prototype.countSegment = function countSegment (p1, p2) {
95964           if (p1.x < this._p.x && p2.x < this._p.x) { return null }
95965           if (this._p.x === p2.x && this._p.y === p2.y) {
95966             this._isPointOnSegment = true;
95967             return null
95968           }
95969           if (p1.y === this._p.y && p2.y === this._p.y) {
95970             var minx = p1.x;
95971             var maxx = p2.x;
95972             if (minx > maxx) {
95973               minx = p2.x;
95974               maxx = p1.x;
95975             }
95976             if (this._p.x >= minx && this._p.x <= maxx) {
95977               this._isPointOnSegment = true;
95978             }
95979             return null
95980           }
95981           if ((p1.y > this._p.y && p2.y <= this._p.y) || (p2.y > this._p.y && p1.y <= this._p.y)) {
95982             var x1 = p1.x - this._p.x;
95983             var y1 = p1.y - this._p.y;
95984             var x2 = p2.x - this._p.x;
95985             var y2 = p2.y - this._p.y;
95986             var xIntSign = RobustDeterminant.signOfDet2x2(x1, y1, x2, y2);
95987             if (xIntSign === 0.0) {
95988               this._isPointOnSegment = true;
95989               return null
95990             }
95991             if (y2 < y1) { xIntSign = -xIntSign; }
95992             if (xIntSign > 0.0) {
95993               this._crossingCount++;
95994             }
95995           }
95996         };
95997         RayCrossingCounter.prototype.isPointInPolygon = function isPointInPolygon () {
95998           return this.getLocation() !== Location.EXTERIOR
95999         };
96000         RayCrossingCounter.prototype.getLocation = function getLocation () {
96001           if (this._isPointOnSegment) { return Location.BOUNDARY }
96002           if (this._crossingCount % 2 === 1) {
96003             return Location.INTERIOR
96004           }
96005           return Location.EXTERIOR
96006         };
96007         RayCrossingCounter.prototype.isOnSegment = function isOnSegment () {
96008           return this._isPointOnSegment
96009         };
96010         RayCrossingCounter.prototype.interfaces_ = function interfaces_ () {
96011           return []
96012         };
96013         RayCrossingCounter.prototype.getClass = function getClass () {
96014           return RayCrossingCounter
96015         };
96016         RayCrossingCounter.locatePointInRing = function locatePointInRing () {
96017           if (arguments[0] instanceof Coordinate && hasInterface(arguments[1], CoordinateSequence)) {
96018             var p = arguments[0];
96019             var ring = arguments[1];
96020             var counter = new RayCrossingCounter(p);
96021             var p1 = new Coordinate();
96022             var p2 = new Coordinate();
96023             for (var i = 1; i < ring.size(); i++) {
96024               ring.getCoordinate(i, p1);
96025               ring.getCoordinate(i - 1, p2);
96026               counter.countSegment(p1, p2);
96027               if (counter.isOnSegment()) { return counter.getLocation() }
96028             }
96029             return counter.getLocation()
96030           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Array) {
96031             var p$1 = arguments[0];
96032             var ring$1 = arguments[1];
96033             var counter$1 = new RayCrossingCounter(p$1);
96034             for (var i$1 = 1; i$1 < ring$1.length; i$1++) {
96035               var p1$1 = ring$1[i$1];
96036               var p2$1 = ring$1[i$1 - 1];
96037               counter$1.countSegment(p1$1, p2$1);
96038               if (counter$1.isOnSegment()) { return counter$1.getLocation() }
96039             }
96040             return counter$1.getLocation()
96041           }
96042         };
96043
96044         var CGAlgorithms = function CGAlgorithms () {};
96045
96046         var staticAccessors$3 = { CLOCKWISE: { configurable: true },RIGHT: { configurable: true },COUNTERCLOCKWISE: { configurable: true },LEFT: { configurable: true },COLLINEAR: { configurable: true },STRAIGHT: { configurable: true } };
96047
96048         CGAlgorithms.prototype.interfaces_ = function interfaces_ () {
96049           return []
96050         };
96051         CGAlgorithms.prototype.getClass = function getClass () {
96052           return CGAlgorithms
96053         };
96054         CGAlgorithms.orientationIndex = function orientationIndex (p1, p2, q) {
96055           return CGAlgorithmsDD.orientationIndex(p1, p2, q)
96056         };
96057         CGAlgorithms.signedArea = function signedArea () {
96058           if (arguments[0] instanceof Array) {
96059             var ring = arguments[0];
96060             if (ring.length < 3) { return 0.0 }
96061             var sum = 0.0;
96062             var x0 = ring[0].x;
96063             for (var i = 1; i < ring.length - 1; i++) {
96064               var x = ring[i].x - x0;
96065               var y1 = ring[i + 1].y;
96066               var y2 = ring[i - 1].y;
96067               sum += x * (y2 - y1);
96068             }
96069             return sum / 2.0
96070           } else if (hasInterface(arguments[0], CoordinateSequence)) {
96071             var ring$1 = arguments[0];
96072             var n = ring$1.size();
96073             if (n < 3) { return 0.0 }
96074             var p0 = new Coordinate();
96075             var p1 = new Coordinate();
96076             var p2 = new Coordinate();
96077             ring$1.getCoordinate(0, p1);
96078             ring$1.getCoordinate(1, p2);
96079             var x0$1 = p1.x;
96080             p2.x -= x0$1;
96081             var sum$1 = 0.0;
96082             for (var i$1 = 1; i$1 < n - 1; i$1++) {
96083               p0.y = p1.y;
96084               p1.x = p2.x;
96085               p1.y = p2.y;
96086               ring$1.getCoordinate(i$1 + 1, p2);
96087               p2.x -= x0$1;
96088               sum$1 += p1.x * (p0.y - p2.y);
96089             }
96090             return sum$1 / 2.0
96091           }
96092         };
96093         CGAlgorithms.distanceLineLine = function distanceLineLine (A, B, C, D) {
96094           if (A.equals(B)) { return CGAlgorithms.distancePointLine(A, C, D) }
96095           if (C.equals(D)) { return CGAlgorithms.distancePointLine(D, A, B) }
96096           var noIntersection = false;
96097           if (!Envelope.intersects(A, B, C, D)) {
96098             noIntersection = true;
96099           } else {
96100             var denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x);
96101             if (denom === 0) {
96102               noIntersection = true;
96103             } else {
96104               var rNumb = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y);
96105               var sNum = (A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.y);
96106               var s = sNum / denom;
96107               var r = rNumb / denom;
96108               if (r < 0 || r > 1 || s < 0 || s > 1) {
96109                 noIntersection = true;
96110               }
96111             }
96112           }
96113           if (noIntersection) {
96114             return MathUtil.min(CGAlgorithms.distancePointLine(A, C, D), CGAlgorithms.distancePointLine(B, C, D), CGAlgorithms.distancePointLine(C, A, B), CGAlgorithms.distancePointLine(D, A, B))
96115           }
96116           return 0.0
96117         };
96118         CGAlgorithms.isPointInRing = function isPointInRing (p, ring) {
96119           return CGAlgorithms.locatePointInRing(p, ring) !== Location.EXTERIOR
96120         };
96121         CGAlgorithms.computeLength = function computeLength (pts) {
96122           var n = pts.size();
96123           if (n <= 1) { return 0.0 }
96124           var len = 0.0;
96125           var p = new Coordinate();
96126           pts.getCoordinate(0, p);
96127           var x0 = p.x;
96128           var y0 = p.y;
96129           for (var i = 1; i < n; i++) {
96130             pts.getCoordinate(i, p);
96131             var x1 = p.x;
96132             var y1 = p.y;
96133             var dx = x1 - x0;
96134             var dy = y1 - y0;
96135             len += Math.sqrt(dx * dx + dy * dy);
96136             x0 = x1;
96137             y0 = y1;
96138           }
96139           return len
96140         };
96141         CGAlgorithms.isCCW = function isCCW (ring) {
96142           var nPts = ring.length - 1;
96143           if (nPts < 3) { throw new IllegalArgumentException('Ring has fewer than 4 points, so orientation cannot be determined') }
96144           var hiPt = ring[0];
96145           var hiIndex = 0;
96146           for (var i = 1; i <= nPts; i++) {
96147             var p = ring[i];
96148             if (p.y > hiPt.y) {
96149               hiPt = p;
96150               hiIndex = i;
96151             }
96152           }
96153           var iPrev = hiIndex;
96154           do {
96155             iPrev = iPrev - 1;
96156             if (iPrev < 0) { iPrev = nPts; }
96157           } while (ring[iPrev].equals2D(hiPt) && iPrev !== hiIndex)
96158           var iNext = hiIndex;
96159           do {
96160             iNext = (iNext + 1) % nPts;
96161           } while (ring[iNext].equals2D(hiPt) && iNext !== hiIndex)
96162           var prev = ring[iPrev];
96163           var next = ring[iNext];
96164           if (prev.equals2D(hiPt) || next.equals2D(hiPt) || prev.equals2D(next)) { return false }
96165           var disc = CGAlgorithms.computeOrientation(prev, hiPt, next);
96166           var isCCW = false;
96167           if (disc === 0) {
96168             isCCW = prev.x > next.x;
96169           } else {
96170             isCCW = disc > 0;
96171           }
96172           return isCCW
96173         };
96174         CGAlgorithms.locatePointInRing = function locatePointInRing (p, ring) {
96175           return RayCrossingCounter.locatePointInRing(p, ring)
96176         };
96177         CGAlgorithms.distancePointLinePerpendicular = function distancePointLinePerpendicular (p, A, B) {
96178           var len2 = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y);
96179           var s = ((A.y - p.y) * (B.x - A.x) - (A.x - p.x) * (B.y - A.y)) / len2;
96180           return Math.abs(s) * Math.sqrt(len2)
96181         };
96182         CGAlgorithms.computeOrientation = function computeOrientation (p1, p2, q) {
96183           return CGAlgorithms.orientationIndex(p1, p2, q)
96184         };
96185         CGAlgorithms.distancePointLine = function distancePointLine () {
96186           if (arguments.length === 2) {
96187             var p = arguments[0];
96188             var line = arguments[1];
96189             if (line.length === 0) { throw new IllegalArgumentException('Line array must contain at least one vertex') }
96190             var minDistance = p.distance(line[0]);
96191             for (var i = 0; i < line.length - 1; i++) {
96192               var dist = CGAlgorithms.distancePointLine(p, line[i], line[i + 1]);
96193               if (dist < minDistance) {
96194                 minDistance = dist;
96195               }
96196             }
96197             return minDistance
96198           } else if (arguments.length === 3) {
96199             var p$1 = arguments[0];
96200             var A = arguments[1];
96201             var B = arguments[2];
96202             if (A.x === B.x && A.y === B.y) { return p$1.distance(A) }
96203             var len2 = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y);
96204             var r = ((p$1.x - A.x) * (B.x - A.x) + (p$1.y - A.y) * (B.y - A.y)) / len2;
96205             if (r <= 0.0) { return p$1.distance(A) }
96206             if (r >= 1.0) { return p$1.distance(B) }
96207             var s = ((A.y - p$1.y) * (B.x - A.x) - (A.x - p$1.x) * (B.y - A.y)) / len2;
96208             return Math.abs(s) * Math.sqrt(len2)
96209           }
96210         };
96211         CGAlgorithms.isOnLine = function isOnLine (p, pt) {
96212           var lineIntersector = new RobustLineIntersector();
96213           for (var i = 1; i < pt.length; i++) {
96214             var p0 = pt[i - 1];
96215             var p1 = pt[i];
96216             lineIntersector.computeIntersection(p, p0, p1);
96217             if (lineIntersector.hasIntersection()) {
96218               return true
96219             }
96220           }
96221           return false
96222         };
96223         staticAccessors$3.CLOCKWISE.get = function () { return -1 };
96224         staticAccessors$3.RIGHT.get = function () { return CGAlgorithms.CLOCKWISE };
96225         staticAccessors$3.COUNTERCLOCKWISE.get = function () { return 1 };
96226         staticAccessors$3.LEFT.get = function () { return CGAlgorithms.COUNTERCLOCKWISE };
96227         staticAccessors$3.COLLINEAR.get = function () { return 0 };
96228         staticAccessors$3.STRAIGHT.get = function () { return CGAlgorithms.COLLINEAR };
96229
96230         Object.defineProperties( CGAlgorithms, staticAccessors$3 );
96231
96232         var GeometryComponentFilter = function GeometryComponentFilter () {};
96233
96234         GeometryComponentFilter.prototype.filter = function filter (geom) {};
96235         GeometryComponentFilter.prototype.interfaces_ = function interfaces_ () {
96236           return []
96237         };
96238         GeometryComponentFilter.prototype.getClass = function getClass () {
96239           return GeometryComponentFilter
96240         };
96241
96242         var Geometry = function Geometry () {
96243           var factory = arguments[0];
96244
96245           this._envelope = null;
96246           this._factory = null;
96247           this._SRID = null;
96248           this._userData = null;
96249           this._factory = factory;
96250           this._SRID = factory.getSRID();
96251         };
96252
96253         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 } };
96254         Geometry.prototype.isGeometryCollection = function isGeometryCollection () {
96255           return this.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION
96256         };
96257         Geometry.prototype.getFactory = function getFactory () {
96258           return this._factory
96259         };
96260         Geometry.prototype.getGeometryN = function getGeometryN (n) {
96261           return this
96262         };
96263         Geometry.prototype.getArea = function getArea () {
96264           return 0.0
96265         };
96266         Geometry.prototype.isRectangle = function isRectangle () {
96267           return false
96268         };
96269         Geometry.prototype.equals = function equals () {
96270           if (arguments[0] instanceof Geometry) {
96271             var g$1 = arguments[0];
96272             if (g$1 === null) { return false }
96273             return this.equalsTopo(g$1)
96274           } else if (arguments[0] instanceof Object) {
96275             var o = arguments[0];
96276             if (!(o instanceof Geometry)) { return false }
96277             var g = o;
96278             return this.equalsExact(g)
96279           }
96280         };
96281         Geometry.prototype.equalsExact = function equalsExact (other) {
96282           return this === other || this.equalsExact(other, 0)
96283         };
96284         Geometry.prototype.geometryChanged = function geometryChanged () {
96285           this.apply(Geometry.geometryChangedFilter);
96286         };
96287         Geometry.prototype.geometryChangedAction = function geometryChangedAction () {
96288           this._envelope = null;
96289         };
96290         Geometry.prototype.equalsNorm = function equalsNorm (g) {
96291           if (g === null) { return false }
96292           return this.norm().equalsExact(g.norm())
96293         };
96294         Geometry.prototype.getLength = function getLength () {
96295           return 0.0
96296         };
96297         Geometry.prototype.getNumGeometries = function getNumGeometries () {
96298           return 1
96299         };
96300         Geometry.prototype.compareTo = function compareTo () {
96301           if (arguments.length === 1) {
96302             var o = arguments[0];
96303             var other = o;
96304             if (this.getSortIndex() !== other.getSortIndex()) {
96305               return this.getSortIndex() - other.getSortIndex()
96306             }
96307             if (this.isEmpty() && other.isEmpty()) {
96308               return 0
96309             }
96310             if (this.isEmpty()) {
96311               return -1
96312             }
96313             if (other.isEmpty()) {
96314               return 1
96315             }
96316             return this.compareToSameClass(o)
96317           } else if (arguments.length === 2) {
96318             var other$1 = arguments[0];
96319             var comp = arguments[1];
96320             if (this.getSortIndex() !== other$1.getSortIndex()) {
96321               return this.getSortIndex() - other$1.getSortIndex()
96322             }
96323             if (this.isEmpty() && other$1.isEmpty()) {
96324               return 0
96325             }
96326             if (this.isEmpty()) {
96327               return -1
96328             }
96329             if (other$1.isEmpty()) {
96330               return 1
96331             }
96332             return this.compareToSameClass(other$1, comp)
96333           }
96334         };
96335         Geometry.prototype.getUserData = function getUserData () {
96336           return this._userData
96337         };
96338         Geometry.prototype.getSRID = function getSRID () {
96339           return this._SRID
96340         };
96341         Geometry.prototype.getEnvelope = function getEnvelope () {
96342           return this.getFactory().toGeometry(this.getEnvelopeInternal())
96343         };
96344         Geometry.prototype.checkNotGeometryCollection = function checkNotGeometryCollection (g) {
96345           if (g.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION) {
96346             throw new IllegalArgumentException('This method does not support GeometryCollection arguments')
96347           }
96348         };
96349         Geometry.prototype.equal = function equal (a, b, tolerance) {
96350           if (tolerance === 0) {
96351             return a.equals(b)
96352           }
96353           return a.distance(b) <= tolerance
96354         };
96355         Geometry.prototype.norm = function norm () {
96356           var copy = this.copy();
96357           copy.normalize();
96358           return copy
96359         };
96360         Geometry.prototype.getPrecisionModel = function getPrecisionModel () {
96361           return this._factory.getPrecisionModel()
96362         };
96363         Geometry.prototype.getEnvelopeInternal = function getEnvelopeInternal () {
96364           if (this._envelope === null) {
96365             this._envelope = this.computeEnvelopeInternal();
96366           }
96367           return new Envelope(this._envelope)
96368         };
96369         Geometry.prototype.setSRID = function setSRID (SRID) {
96370           this._SRID = SRID;
96371         };
96372         Geometry.prototype.setUserData = function setUserData (userData) {
96373           this._userData = userData;
96374         };
96375         Geometry.prototype.compare = function compare (a, b) {
96376           var i = a.iterator();
96377           var j = b.iterator();
96378           while (i.hasNext() && j.hasNext()) {
96379             var aElement = i.next();
96380             var bElement = j.next();
96381             var comparison = aElement.compareTo(bElement);
96382             if (comparison !== 0) {
96383               return comparison
96384             }
96385           }
96386           if (i.hasNext()) {
96387             return 1
96388           }
96389           if (j.hasNext()) {
96390             return -1
96391           }
96392           return 0
96393         };
96394         Geometry.prototype.hashCode = function hashCode () {
96395           return this.getEnvelopeInternal().hashCode()
96396         };
96397         Geometry.prototype.isGeometryCollectionOrDerived = function isGeometryCollectionOrDerived () {
96398           if (this.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION || this.getSortIndex() === Geometry.SORTINDEX_MULTIPOINT || this.getSortIndex() === Geometry.SORTINDEX_MULTILINESTRING || this.getSortIndex() === Geometry.SORTINDEX_MULTIPOLYGON) {
96399             return true
96400           }
96401           return false
96402         };
96403         Geometry.prototype.interfaces_ = function interfaces_ () {
96404           return [Clonable, Comparable, Serializable]
96405         };
96406         Geometry.prototype.getClass = function getClass () {
96407           return Geometry
96408         };
96409         Geometry.hasNonEmptyElements = function hasNonEmptyElements (geometries) {
96410           for (var i = 0; i < geometries.length; i++) {
96411             if (!geometries[i].isEmpty()) {
96412               return true
96413             }
96414           }
96415           return false
96416         };
96417         Geometry.hasNullElements = function hasNullElements (array) {
96418           for (var i = 0; i < array.length; i++) {
96419             if (array[i] === null) {
96420               return true
96421             }
96422           }
96423           return false
96424         };
96425         staticAccessors$11.serialVersionUID.get = function () { return 8763622679187376702 };
96426         staticAccessors$11.SORTINDEX_POINT.get = function () { return 0 };
96427         staticAccessors$11.SORTINDEX_MULTIPOINT.get = function () { return 1 };
96428         staticAccessors$11.SORTINDEX_LINESTRING.get = function () { return 2 };
96429         staticAccessors$11.SORTINDEX_LINEARRING.get = function () { return 3 };
96430         staticAccessors$11.SORTINDEX_MULTILINESTRING.get = function () { return 4 };
96431         staticAccessors$11.SORTINDEX_POLYGON.get = function () { return 5 };
96432         staticAccessors$11.SORTINDEX_MULTIPOLYGON.get = function () { return 6 };
96433         staticAccessors$11.SORTINDEX_GEOMETRYCOLLECTION.get = function () { return 7 };
96434         staticAccessors$11.geometryChangedFilter.get = function () { return geometryChangedFilter };
96435
96436         Object.defineProperties( Geometry, staticAccessors$11 );
96437
96438         var geometryChangedFilter = function geometryChangedFilter () {};
96439
96440         geometryChangedFilter.interfaces_ = function interfaces_ () {
96441           return [GeometryComponentFilter]
96442         };
96443         geometryChangedFilter.filter = function filter (geom) {
96444           geom.geometryChangedAction();
96445         };
96446
96447         var CoordinateFilter = function CoordinateFilter () {};
96448
96449         CoordinateFilter.prototype.filter = function filter (coord) {};
96450         CoordinateFilter.prototype.interfaces_ = function interfaces_ () {
96451           return []
96452         };
96453         CoordinateFilter.prototype.getClass = function getClass () {
96454           return CoordinateFilter
96455         };
96456
96457         var BoundaryNodeRule = function BoundaryNodeRule () {};
96458
96459         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 } };
96460
96461         BoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {};
96462         BoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96463           return []
96464         };
96465         BoundaryNodeRule.prototype.getClass = function getClass () {
96466           return BoundaryNodeRule
96467         };
96468         staticAccessors$12.Mod2BoundaryNodeRule.get = function () { return Mod2BoundaryNodeRule };
96469         staticAccessors$12.EndPointBoundaryNodeRule.get = function () { return EndPointBoundaryNodeRule };
96470         staticAccessors$12.MultiValentEndPointBoundaryNodeRule.get = function () { return MultiValentEndPointBoundaryNodeRule };
96471         staticAccessors$12.MonoValentEndPointBoundaryNodeRule.get = function () { return MonoValentEndPointBoundaryNodeRule };
96472         staticAccessors$12.MOD2_BOUNDARY_RULE.get = function () { return new Mod2BoundaryNodeRule() };
96473         staticAccessors$12.ENDPOINT_BOUNDARY_RULE.get = function () { return new EndPointBoundaryNodeRule() };
96474         staticAccessors$12.MULTIVALENT_ENDPOINT_BOUNDARY_RULE.get = function () { return new MultiValentEndPointBoundaryNodeRule() };
96475         staticAccessors$12.MONOVALENT_ENDPOINT_BOUNDARY_RULE.get = function () { return new MonoValentEndPointBoundaryNodeRule() };
96476         staticAccessors$12.OGC_SFS_BOUNDARY_RULE.get = function () { return BoundaryNodeRule.MOD2_BOUNDARY_RULE };
96477
96478         Object.defineProperties( BoundaryNodeRule, staticAccessors$12 );
96479
96480         var Mod2BoundaryNodeRule = function Mod2BoundaryNodeRule () {};
96481
96482         Mod2BoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96483           return boundaryCount % 2 === 1
96484         };
96485         Mod2BoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96486           return [BoundaryNodeRule]
96487         };
96488         Mod2BoundaryNodeRule.prototype.getClass = function getClass () {
96489           return Mod2BoundaryNodeRule
96490         };
96491
96492         var EndPointBoundaryNodeRule = function EndPointBoundaryNodeRule () {};
96493
96494         EndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96495           return boundaryCount > 0
96496         };
96497         EndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96498           return [BoundaryNodeRule]
96499         };
96500         EndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96501           return EndPointBoundaryNodeRule
96502         };
96503
96504         var MultiValentEndPointBoundaryNodeRule = function MultiValentEndPointBoundaryNodeRule () {};
96505
96506         MultiValentEndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96507           return boundaryCount > 1
96508         };
96509         MultiValentEndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96510           return [BoundaryNodeRule]
96511         };
96512         MultiValentEndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96513           return MultiValentEndPointBoundaryNodeRule
96514         };
96515
96516         var MonoValentEndPointBoundaryNodeRule = function MonoValentEndPointBoundaryNodeRule () {};
96517
96518         MonoValentEndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96519           return boundaryCount === 1
96520         };
96521         MonoValentEndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96522           return [BoundaryNodeRule]
96523         };
96524         MonoValentEndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96525           return MonoValentEndPointBoundaryNodeRule
96526         };
96527
96528         // import Iterator from './Iterator'
96529
96530         /**
96531          * @see http://download.oracle.com/javase/6/docs/api/java/util/Collection.html
96532          *
96533          * @constructor
96534          * @private
96535          */
96536         var Collection = function Collection () {};
96537
96538         Collection.prototype.add = function add () {};
96539
96540         /**
96541          * Appends all of the elements in the specified collection to the end of this
96542          * list, in the order that they are returned by the specified collection's
96543          * iterator (optional operation).
96544          * @param {javascript.util.Collection} c
96545          * @return {boolean}
96546          */
96547         Collection.prototype.addAll = function addAll () {};
96548
96549         /**
96550          * Returns true if this collection contains no elements.
96551          * @return {boolean}
96552          */
96553         Collection.prototype.isEmpty = function isEmpty () {};
96554
96555         /**
96556          * Returns an iterator over the elements in this collection.
96557          * @return {javascript.util.Iterator}
96558          */
96559         Collection.prototype.iterator = function iterator () {};
96560
96561         /**
96562          * Returns an iterator over the elements in this collection.
96563          * @return {number}
96564          */
96565         Collection.prototype.size = function size () {};
96566
96567         /**
96568          * Returns an array containing all of the elements in this collection.
96569          * @return {Array}
96570          */
96571         Collection.prototype.toArray = function toArray () {};
96572
96573         /**
96574          * Removes a single instance of the specified element from this collection if it
96575          * is present. (optional)
96576          * @param {Object} e
96577          * @return {boolean}
96578          */
96579         Collection.prototype.remove = function remove () {};
96580
96581         /**
96582          * @param {string=} message Optional message
96583          * @extends {Error}
96584          * @constructor
96585          * @private
96586          */
96587         function IndexOutOfBoundsException (message) {
96588           this.message = message || '';
96589         }
96590         IndexOutOfBoundsException.prototype = new Error();
96591
96592         /**
96593          * @type {string}
96594          */
96595         IndexOutOfBoundsException.prototype.name = 'IndexOutOfBoundsException';
96596
96597         /**
96598          * @see http://download.oracle.com/javase/6/docs/api/java/util/Iterator.html
96599          * @constructor
96600          * @private
96601          */
96602         var Iterator$1 = function Iterator () {};
96603
96604         Iterator$1.prototype.hasNext = function hasNext () {};
96605
96606         /**
96607          * Returns the next element in the iteration.
96608          * @return {Object}
96609          */
96610         Iterator$1.prototype.next = function next () {};
96611
96612         /**
96613          * Removes from the underlying collection the last element returned by the
96614          * iterator (optional operation).
96615          */
96616         Iterator$1.prototype.remove = function remove () {};
96617
96618         /**
96619          * @see http://download.oracle.com/javase/6/docs/api/java/util/List.html
96620          *
96621          * @extends {javascript.util.Collection}
96622          * @constructor
96623          * @private
96624          */
96625         var List = (function (Collection$$1) {
96626           function List () {
96627             Collection$$1.apply(this, arguments);
96628           }
96629
96630           if ( Collection$$1 ) List.__proto__ = Collection$$1;
96631           List.prototype = Object.create( Collection$$1 && Collection$$1.prototype );
96632           List.prototype.constructor = List;
96633
96634           List.prototype.get = function get () { };
96635
96636           /**
96637            * Replaces the element at the specified position in this list with the
96638            * specified element (optional operation).
96639            * @param {number} index
96640            * @param {Object} e
96641            * @return {Object}
96642            */
96643           List.prototype.set = function set () { };
96644
96645           /**
96646            * Returns true if this collection contains no elements.
96647            * @return {boolean}
96648            */
96649           List.prototype.isEmpty = function isEmpty () { };
96650
96651           return List;
96652         }(Collection));
96653
96654         /**
96655          * @param {string=} message Optional message
96656          * @extends {Error}
96657          * @constructor
96658          * @private
96659          */
96660         function NoSuchElementException (message) {
96661           this.message = message || '';
96662         }
96663         NoSuchElementException.prototype = new Error();
96664
96665         /**
96666          * @type {string}
96667          */
96668         NoSuchElementException.prototype.name = 'NoSuchElementException';
96669
96670         // import OperationNotSupported from './OperationNotSupported'
96671
96672         /**
96673          * @see http://download.oracle.com/javase/6/docs/api/java/util/ArrayList.html
96674          *
96675          * @extends List
96676          * @private
96677          */
96678         var ArrayList = (function (List$$1) {
96679           function ArrayList () {
96680             List$$1.call(this);
96681             this.array_ = [];
96682
96683             if (arguments[0] instanceof Collection) {
96684               this.addAll(arguments[0]);
96685             }
96686           }
96687
96688           if ( List$$1 ) ArrayList.__proto__ = List$$1;
96689           ArrayList.prototype = Object.create( List$$1 && List$$1.prototype );
96690           ArrayList.prototype.constructor = ArrayList;
96691
96692           ArrayList.prototype.ensureCapacity = function ensureCapacity () {};
96693           ArrayList.prototype.interfaces_ = function interfaces_ () { return [List$$1, Collection] };
96694
96695           /**
96696            * @override
96697            */
96698           ArrayList.prototype.add = function add (e) {
96699             if (arguments.length === 1) {
96700               this.array_.push(e);
96701             } else {
96702               this.array_.splice(arguments[0], arguments[1]);
96703             }
96704             return true
96705           };
96706
96707           ArrayList.prototype.clear = function clear () {
96708             this.array_ = [];
96709           };
96710
96711           /**
96712            * @override
96713            */
96714           ArrayList.prototype.addAll = function addAll (c) {
96715             var this$1 = this;
96716
96717             for (var i = c.iterator(); i.hasNext();) {
96718               this$1.add(i.next());
96719             }
96720             return true
96721           };
96722
96723           /**
96724            * @override
96725            */
96726           ArrayList.prototype.set = function set (index, element) {
96727             var oldElement = this.array_[index];
96728             this.array_[index] = element;
96729             return oldElement
96730           };
96731
96732           /**
96733            * @override
96734            */
96735           ArrayList.prototype.iterator = function iterator () {
96736             return new Iterator_(this)
96737           };
96738
96739           /**
96740            * @override
96741            */
96742           ArrayList.prototype.get = function get (index) {
96743             if (index < 0 || index >= this.size()) {
96744               throw new IndexOutOfBoundsException()
96745             }
96746
96747             return this.array_[index]
96748           };
96749
96750           /**
96751            * @override
96752            */
96753           ArrayList.prototype.isEmpty = function isEmpty () {
96754             return this.array_.length === 0
96755           };
96756
96757           /**
96758            * @override
96759            */
96760           ArrayList.prototype.size = function size () {
96761             return this.array_.length
96762           };
96763
96764           /**
96765            * @override
96766            */
96767           ArrayList.prototype.toArray = function toArray () {
96768             var this$1 = this;
96769
96770             var array = [];
96771
96772             for (var i = 0, len = this.array_.length; i < len; i++) {
96773               array.push(this$1.array_[i]);
96774             }
96775
96776             return array
96777           };
96778
96779           /**
96780            * @override
96781            */
96782           ArrayList.prototype.remove = function remove (o) {
96783             var this$1 = this;
96784
96785             var found = false;
96786
96787             for (var i = 0, len = this.array_.length; i < len; i++) {
96788               if (this$1.array_[i] === o) {
96789                 this$1.array_.splice(i, 1);
96790                 found = true;
96791                 break
96792               }
96793             }
96794
96795             return found
96796           };
96797
96798           return ArrayList;
96799         }(List));
96800
96801         /**
96802          * @extends {Iterator}
96803          * @param {ArrayList} arrayList
96804          * @constructor
96805          * @private
96806          */
96807         var Iterator_ = (function (Iterator$$1) {
96808           function Iterator_ (arrayList) {
96809             Iterator$$1.call(this);
96810             /**
96811              * @type {ArrayList}
96812              * @private
96813             */
96814             this.arrayList_ = arrayList;
96815             /**
96816              * @type {number}
96817              * @private
96818             */
96819             this.position_ = 0;
96820           }
96821
96822           if ( Iterator$$1 ) Iterator_.__proto__ = Iterator$$1;
96823           Iterator_.prototype = Object.create( Iterator$$1 && Iterator$$1.prototype );
96824           Iterator_.prototype.constructor = Iterator_;
96825
96826           /**
96827            * @override
96828            */
96829           Iterator_.prototype.next = function next () {
96830             if (this.position_ === this.arrayList_.size()) {
96831               throw new NoSuchElementException()
96832             }
96833             return this.arrayList_.get(this.position_++)
96834           };
96835
96836           /**
96837            * @override
96838            */
96839           Iterator_.prototype.hasNext = function hasNext () {
96840             if (this.position_ < this.arrayList_.size()) {
96841               return true
96842             } else {
96843               return false
96844             }
96845           };
96846
96847           /**
96848            * TODO: should be in ListIterator
96849            * @override
96850            */
96851           Iterator_.prototype.set = function set (element) {
96852             return this.arrayList_.set(this.position_ - 1, element)
96853           };
96854
96855           /**
96856            * @override
96857            */
96858           Iterator_.prototype.remove = function remove () {
96859             this.arrayList_.remove(this.arrayList_.get(this.position_));
96860           };
96861
96862           return Iterator_;
96863         }(Iterator$1));
96864
96865         var CoordinateList = (function (ArrayList$$1) {
96866           function CoordinateList () {
96867             ArrayList$$1.call(this);
96868             if (arguments.length === 0) ; else if (arguments.length === 1) {
96869               var coord = arguments[0];
96870               this.ensureCapacity(coord.length);
96871               this.add(coord, true);
96872             } else if (arguments.length === 2) {
96873               var coord$1 = arguments[0];
96874               var allowRepeated = arguments[1];
96875               this.ensureCapacity(coord$1.length);
96876               this.add(coord$1, allowRepeated);
96877             }
96878           }
96879
96880           if ( ArrayList$$1 ) CoordinateList.__proto__ = ArrayList$$1;
96881           CoordinateList.prototype = Object.create( ArrayList$$1 && ArrayList$$1.prototype );
96882           CoordinateList.prototype.constructor = CoordinateList;
96883
96884           var staticAccessors = { coordArrayType: { configurable: true } };
96885           staticAccessors.coordArrayType.get = function () { return new Array(0).fill(null) };
96886           CoordinateList.prototype.getCoordinate = function getCoordinate (i) {
96887             return this.get(i)
96888           };
96889           CoordinateList.prototype.addAll = function addAll () {
96890             var this$1 = this;
96891
96892             if (arguments.length === 2) {
96893               var coll = arguments[0];
96894               var allowRepeated = arguments[1];
96895               var isChanged = false;
96896               for (var i = coll.iterator(); i.hasNext();) {
96897                 this$1.add(i.next(), allowRepeated);
96898                 isChanged = true;
96899               }
96900               return isChanged
96901             } else { return ArrayList$$1.prototype.addAll.apply(this, arguments) }
96902           };
96903           CoordinateList.prototype.clone = function clone () {
96904             var this$1 = this;
96905
96906             var clone = ArrayList$$1.prototype.clone.call(this);
96907             for (var i = 0; i < this.size(); i++) {
96908               clone.add(i, this$1.get(i).copy());
96909             }
96910             return clone
96911           };
96912           CoordinateList.prototype.toCoordinateArray = function toCoordinateArray () {
96913             return this.toArray(CoordinateList.coordArrayType)
96914           };
96915           CoordinateList.prototype.add = function add () {
96916             var this$1 = this;
96917
96918             if (arguments.length === 1) {
96919               var coord = arguments[0];
96920               ArrayList$$1.prototype.add.call(this, coord);
96921             } else if (arguments.length === 2) {
96922               if (arguments[0] instanceof Array && typeof arguments[1] === 'boolean') {
96923                 var coord$1 = arguments[0];
96924                 var allowRepeated = arguments[1];
96925                 this.add(coord$1, allowRepeated, true);
96926                 return true
96927               } else if (arguments[0] instanceof Coordinate && typeof arguments[1] === 'boolean') {
96928                 var coord$2 = arguments[0];
96929                 var allowRepeated$1 = arguments[1];
96930                 if (!allowRepeated$1) {
96931                   if (this.size() >= 1) {
96932                     var last = this.get(this.size() - 1);
96933                     if (last.equals2D(coord$2)) { return null }
96934                   }
96935                 }
96936                 ArrayList$$1.prototype.add.call(this, coord$2);
96937               } else if (arguments[0] instanceof Object && typeof arguments[1] === 'boolean') {
96938                 var obj = arguments[0];
96939                 var allowRepeated$2 = arguments[1];
96940                 this.add(obj, allowRepeated$2);
96941                 return true
96942               }
96943             } else if (arguments.length === 3) {
96944               if (typeof arguments[2] === 'boolean' && (arguments[0] instanceof Array && typeof arguments[1] === 'boolean')) {
96945                 var coord$3 = arguments[0];
96946                 var allowRepeated$3 = arguments[1];
96947                 var direction = arguments[2];
96948                 if (direction) {
96949                   for (var i$1 = 0; i$1 < coord$3.length; i$1++) {
96950                     this$1.add(coord$3[i$1], allowRepeated$3);
96951                   }
96952                 } else {
96953                   for (var i$2 = coord$3.length - 1; i$2 >= 0; i$2--) {
96954                     this$1.add(coord$3[i$2], allowRepeated$3);
96955                   }
96956                 }
96957                 return true
96958               } else if (typeof arguments[2] === 'boolean' && (Number.isInteger(arguments[0]) && arguments[1] instanceof Coordinate)) {
96959                 var i$3 = arguments[0];
96960                 var coord$4 = arguments[1];
96961                 var allowRepeated$4 = arguments[2];
96962                 if (!allowRepeated$4) {
96963                   var size = this.size();
96964                   if (size > 0) {
96965                     if (i$3 > 0) {
96966                       var prev = this.get(i$3 - 1);
96967                       if (prev.equals2D(coord$4)) { return null }
96968                     }
96969                     if (i$3 < size) {
96970                       var next = this.get(i$3);
96971                       if (next.equals2D(coord$4)) { return null }
96972                     }
96973                   }
96974                 }
96975                 ArrayList$$1.prototype.add.call(this, i$3, coord$4);
96976               }
96977             } else if (arguments.length === 4) {
96978               var coord$5 = arguments[0];
96979               var allowRepeated$5 = arguments[1];
96980               var start = arguments[2];
96981               var end = arguments[3];
96982               var inc = 1;
96983               if (start > end) { inc = -1; }
96984               for (var i = start; i !== end; i += inc) {
96985                 this$1.add(coord$5[i], allowRepeated$5);
96986               }
96987               return true
96988             }
96989           };
96990           CoordinateList.prototype.closeRing = function closeRing () {
96991             if (this.size() > 0) { this.add(new Coordinate(this.get(0)), false); }
96992           };
96993           CoordinateList.prototype.interfaces_ = function interfaces_ () {
96994             return []
96995           };
96996           CoordinateList.prototype.getClass = function getClass () {
96997             return CoordinateList
96998           };
96999
97000           Object.defineProperties( CoordinateList, staticAccessors );
97001
97002           return CoordinateList;
97003         }(ArrayList));
97004
97005         var CoordinateArrays = function CoordinateArrays () {};
97006
97007         var staticAccessors$13 = { ForwardComparator: { configurable: true },BidirectionalComparator: { configurable: true },coordArrayType: { configurable: true } };
97008
97009         staticAccessors$13.ForwardComparator.get = function () { return ForwardComparator };
97010         staticAccessors$13.BidirectionalComparator.get = function () { return BidirectionalComparator };
97011         staticAccessors$13.coordArrayType.get = function () { return new Array(0).fill(null) };
97012
97013         CoordinateArrays.prototype.interfaces_ = function interfaces_ () {
97014           return []
97015         };
97016         CoordinateArrays.prototype.getClass = function getClass () {
97017           return CoordinateArrays
97018         };
97019         CoordinateArrays.isRing = function isRing (pts) {
97020           if (pts.length < 4) { return false }
97021           if (!pts[0].equals2D(pts[pts.length - 1])) { return false }
97022           return true
97023         };
97024         CoordinateArrays.ptNotInList = function ptNotInList (testPts, pts) {
97025           for (var i = 0; i < testPts.length; i++) {
97026             var testPt = testPts[i];
97027             if (CoordinateArrays.indexOf(testPt, pts) < 0) { return testPt }
97028           }
97029           return null
97030         };
97031         CoordinateArrays.scroll = function scroll (coordinates, firstCoordinate) {
97032           var i = CoordinateArrays.indexOf(firstCoordinate, coordinates);
97033           if (i < 0) { return null }
97034           var newCoordinates = new Array(coordinates.length).fill(null);
97035           System.arraycopy(coordinates, i, newCoordinates, 0, coordinates.length - i);
97036           System.arraycopy(coordinates, 0, newCoordinates, coordinates.length - i, i);
97037           System.arraycopy(newCoordinates, 0, coordinates, 0, coordinates.length);
97038         };
97039         CoordinateArrays.equals = function equals () {
97040           if (arguments.length === 2) {
97041             var coord1 = arguments[0];
97042             var coord2 = arguments[1];
97043             if (coord1 === coord2) { return true }
97044             if (coord1 === null || coord2 === null) { return false }
97045             if (coord1.length !== coord2.length) { return false }
97046             for (var i = 0; i < coord1.length; i++) {
97047               if (!coord1[i].equals(coord2[i])) { return false }
97048             }
97049             return true
97050           } else if (arguments.length === 3) {
97051             var coord1$1 = arguments[0];
97052             var coord2$1 = arguments[1];
97053             var coordinateComparator = arguments[2];
97054             if (coord1$1 === coord2$1) { return true }
97055             if (coord1$1 === null || coord2$1 === null) { return false }
97056             if (coord1$1.length !== coord2$1.length) { return false }
97057             for (var i$1 = 0; i$1 < coord1$1.length; i$1++) {
97058               if (coordinateComparator.compare(coord1$1[i$1], coord2$1[i$1]) !== 0) { return false }
97059             }
97060             return true
97061           }
97062         };
97063         CoordinateArrays.intersection = function intersection (coordinates, env) {
97064           var coordList = new CoordinateList();
97065           for (var i = 0; i < coordinates.length; i++) {
97066             if (env.intersects(coordinates[i])) { coordList.add(coordinates[i], true); }
97067           }
97068           return coordList.toCoordinateArray()
97069         };
97070         CoordinateArrays.hasRepeatedPoints = function hasRepeatedPoints (coord) {
97071           for (var i = 1; i < coord.length; i++) {
97072             if (coord[i - 1].equals(coord[i])) {
97073               return true
97074             }
97075           }
97076           return false
97077         };
97078         CoordinateArrays.removeRepeatedPoints = function removeRepeatedPoints (coord) {
97079           if (!CoordinateArrays.hasRepeatedPoints(coord)) { return coord }
97080           var coordList = new CoordinateList(coord, false);
97081           return coordList.toCoordinateArray()
97082         };
97083         CoordinateArrays.reverse = function reverse (coord) {
97084           var last = coord.length - 1;
97085           var mid = Math.trunc(last / 2);
97086           for (var i = 0; i <= mid; i++) {
97087             var tmp = coord[i];
97088             coord[i] = coord[last - i];
97089             coord[last - i] = tmp;
97090           }
97091         };
97092         CoordinateArrays.removeNull = function removeNull (coord) {
97093           var nonNull = 0;
97094           for (var i = 0; i < coord.length; i++) {
97095             if (coord[i] !== null) { nonNull++; }
97096           }
97097           var newCoord = new Array(nonNull).fill(null);
97098           if (nonNull === 0) { return newCoord }
97099           var j = 0;
97100           for (var i$1 = 0; i$1 < coord.length; i$1++) {
97101             if (coord[i$1] !== null) { newCoord[j++] = coord[i$1]; }
97102           }
97103           return newCoord
97104         };
97105         CoordinateArrays.copyDeep = function copyDeep () {
97106           if (arguments.length === 1) {
97107             var coordinates = arguments[0];
97108             var copy = new Array(coordinates.length).fill(null);
97109             for (var i = 0; i < coordinates.length; i++) {
97110               copy[i] = new Coordinate(coordinates[i]);
97111             }
97112             return copy
97113           } else if (arguments.length === 5) {
97114             var src = arguments[0];
97115             var srcStart = arguments[1];
97116             var dest = arguments[2];
97117             var destStart = arguments[3];
97118             var length = arguments[4];
97119             for (var i$1 = 0; i$1 < length; i$1++) {
97120               dest[destStart + i$1] = new Coordinate(src[srcStart + i$1]);
97121             }
97122           }
97123         };
97124         CoordinateArrays.isEqualReversed = function isEqualReversed (pts1, pts2) {
97125           for (var i = 0; i < pts1.length; i++) {
97126             var p1 = pts1[i];
97127             var p2 = pts2[pts1.length - i - 1];
97128             if (p1.compareTo(p2) !== 0) { return false }
97129           }
97130           return true
97131         };
97132         CoordinateArrays.envelope = function envelope (coordinates) {
97133           var env = new Envelope();
97134           for (var i = 0; i < coordinates.length; i++) {
97135             env.expandToInclude(coordinates[i]);
97136           }
97137           return env
97138         };
97139         CoordinateArrays.toCoordinateArray = function toCoordinateArray (coordList) {
97140           return coordList.toArray(CoordinateArrays.coordArrayType)
97141         };
97142         CoordinateArrays.atLeastNCoordinatesOrNothing = function atLeastNCoordinatesOrNothing (n, c) {
97143           return c.length >= n ? c : []
97144         };
97145         CoordinateArrays.indexOf = function indexOf (coordinate, coordinates) {
97146           for (var i = 0; i < coordinates.length; i++) {
97147             if (coordinate.equals(coordinates[i])) {
97148               return i
97149             }
97150           }
97151           return -1
97152         };
97153         CoordinateArrays.increasingDirection = function increasingDirection (pts) {
97154           for (var i = 0; i < Math.trunc(pts.length / 2); i++) {
97155             var j = pts.length - 1 - i;
97156             var comp = pts[i].compareTo(pts[j]);
97157             if (comp !== 0) { return comp }
97158           }
97159           return 1
97160         };
97161         CoordinateArrays.compare = function compare (pts1, pts2) {
97162           var i = 0;
97163           while (i < pts1.length && i < pts2.length) {
97164             var compare = pts1[i].compareTo(pts2[i]);
97165             if (compare !== 0) { return compare }
97166             i++;
97167           }
97168           if (i < pts2.length) { return -1 }
97169           if (i < pts1.length) { return 1 }
97170           return 0
97171         };
97172         CoordinateArrays.minCoordinate = function minCoordinate (coordinates) {
97173           var minCoord = null;
97174           for (var i = 0; i < coordinates.length; i++) {
97175             if (minCoord === null || minCoord.compareTo(coordinates[i]) > 0) {
97176               minCoord = coordinates[i];
97177             }
97178           }
97179           return minCoord
97180         };
97181         CoordinateArrays.extract = function extract (pts, start, end) {
97182           start = MathUtil.clamp(start, 0, pts.length);
97183           end = MathUtil.clamp(end, -1, pts.length);
97184           var npts = end - start + 1;
97185           if (end < 0) { npts = 0; }
97186           if (start >= pts.length) { npts = 0; }
97187           if (end < start) { npts = 0; }
97188           var extractPts = new Array(npts).fill(null);
97189           if (npts === 0) { return extractPts }
97190           var iPts = 0;
97191           for (var i = start; i <= end; i++) {
97192             extractPts[iPts++] = pts[i];
97193           }
97194           return extractPts
97195         };
97196
97197         Object.defineProperties( CoordinateArrays, staticAccessors$13 );
97198
97199         var ForwardComparator = function ForwardComparator () {};
97200
97201         ForwardComparator.prototype.compare = function compare (o1, o2) {
97202           var pts1 = o1;
97203           var pts2 = o2;
97204           return CoordinateArrays.compare(pts1, pts2)
97205         };
97206         ForwardComparator.prototype.interfaces_ = function interfaces_ () {
97207           return [Comparator]
97208         };
97209         ForwardComparator.prototype.getClass = function getClass () {
97210           return ForwardComparator
97211         };
97212
97213         var BidirectionalComparator = function BidirectionalComparator () {};
97214
97215         BidirectionalComparator.prototype.compare = function compare (o1, o2) {
97216           var pts1 = o1;
97217           var pts2 = o2;
97218           if (pts1.length < pts2.length) { return -1 }
97219           if (pts1.length > pts2.length) { return 1 }
97220           if (pts1.length === 0) { return 0 }
97221           var forwardComp = CoordinateArrays.compare(pts1, pts2);
97222           var isEqualRev = CoordinateArrays.isEqualReversed(pts1, pts2);
97223           if (isEqualRev) { return 0 }
97224           return forwardComp
97225         };
97226         BidirectionalComparator.prototype.OLDcompare = function OLDcompare (o1, o2) {
97227           var pts1 = o1;
97228           var pts2 = o2;
97229           if (pts1.length < pts2.length) { return -1 }
97230           if (pts1.length > pts2.length) { return 1 }
97231           if (pts1.length === 0) { return 0 }
97232           var dir1 = CoordinateArrays.increasingDirection(pts1);
97233           var dir2 = CoordinateArrays.increasingDirection(pts2);
97234           var i1 = dir1 > 0 ? 0 : pts1.length - 1;
97235           var i2 = dir2 > 0 ? 0 : pts1.length - 1;
97236           for (var i = 0; i < pts1.length; i++) {
97237             var comparePt = pts1[i1].compareTo(pts2[i2]);
97238             if (comparePt !== 0) { return comparePt }
97239             i1 += dir1;
97240             i2 += dir2;
97241           }
97242           return 0
97243         };
97244         BidirectionalComparator.prototype.interfaces_ = function interfaces_ () {
97245           return [Comparator]
97246         };
97247         BidirectionalComparator.prototype.getClass = function getClass () {
97248           return BidirectionalComparator
97249         };
97250
97251         /**
97252          * @see http://download.oracle.com/javase/6/docs/api/java/util/Map.html
97253          *
97254          * @constructor
97255          * @private
97256          */
97257         var Map$1$1 = function Map () {};
97258
97259         Map$1$1.prototype.get = function get () {};
97260         /**
97261          * Associates the specified value with the specified key in this map (optional
97262          * operation).
97263          * @param {Object} key
97264          * @param {Object} value
97265          * @return {Object}
97266          */
97267         Map$1$1.prototype.put = function put () {};
97268
97269         /**
97270          * Returns the number of key-value mappings in this map.
97271          * @return {number}
97272          */
97273         Map$1$1.prototype.size = function size () {};
97274
97275         /**
97276          * Returns a Collection view of the values contained in this map.
97277          * @return {javascript.util.Collection}
97278          */
97279         Map$1$1.prototype.values = function values () {};
97280
97281         /**
97282          * Returns a {@link Set} view of the mappings contained in this map.
97283          * The set is backed by the map, so changes to the map are
97284          * reflected in the set, and vice-versa.If the map is modified
97285          * while an iteration over the set is in progress (except through
97286          * the iterator's own <tt>remove</tt> operation, or through the
97287          * <tt>setValue</tt> operation on a map entry returned by the
97288          * iterator) the results of the iteration are undefined.The set
97289          * supports element removal, which removes the corresponding
97290          * mapping from the map, via the <tt>Iterator.remove</tt>,
97291          * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
97292          * <tt>clear</tt> operations.It does not support the
97293          * <tt>add</tt> or <tt>addAll</tt> operations.
97294          *
97295          * @return {Set} a set view of the mappings contained in this map
97296          */
97297         Map$1$1.prototype.entrySet = function entrySet () {};
97298
97299         /**
97300          * @see http://download.oracle.com/javase/6/docs/api/java/util/SortedMap.html
97301          *
97302          * @extends {Map}
97303          * @constructor
97304          * @private
97305          */
97306         var SortedMap = (function (Map) {
97307                 function SortedMap () {
97308                         Map.apply(this, arguments);
97309                 }if ( Map ) SortedMap.__proto__ = Map;
97310                 SortedMap.prototype = Object.create( Map && Map.prototype );
97311                 SortedMap.prototype.constructor = SortedMap;
97312
97313                 
97314
97315                 return SortedMap;
97316         }(Map$1$1));
97317
97318         /**
97319          * @param {string=} message Optional message
97320          * @extends {Error}
97321          * @constructor
97322          * @private
97323          */
97324         function OperationNotSupported (message) {
97325           this.message = message || '';
97326         }
97327         OperationNotSupported.prototype = new Error();
97328
97329         /**
97330          * @type {string}
97331          */
97332         OperationNotSupported.prototype.name = 'OperationNotSupported';
97333
97334         /**
97335          * @see http://download.oracle.com/javase/6/docs/api/java/util/Set.html
97336          *
97337          * @extends {Collection}
97338          * @constructor
97339          * @private
97340          */
97341         function Set$2() {}
97342         Set$2.prototype = new Collection();
97343
97344
97345         /**
97346          * Returns true if this set contains the specified element. More formally,
97347          * returns true if and only if this set contains an element e such that (o==null ?
97348          * e==null : o.equals(e)).
97349          * @param {Object} e
97350          * @return {boolean}
97351          */
97352         Set$2.prototype.contains = function() {};
97353
97354         /**
97355          * @see http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html
97356          *
97357          * @extends {javascript.util.Set}
97358          * @constructor
97359          * @private
97360          */
97361         var HashSet = (function (Set$$1) {
97362           function HashSet () {
97363             Set$$1.call(this);
97364             this.array_ = [];
97365
97366             if (arguments[0] instanceof Collection) {
97367               this.addAll(arguments[0]);
97368             }
97369           }
97370
97371           if ( Set$$1 ) HashSet.__proto__ = Set$$1;
97372           HashSet.prototype = Object.create( Set$$1 && Set$$1.prototype );
97373           HashSet.prototype.constructor = HashSet;
97374
97375           /**
97376            * @override
97377            */
97378           HashSet.prototype.contains = function contains (o) {
97379             var this$1 = this;
97380
97381             for (var i = 0, len = this.array_.length; i < len; i++) {
97382               var e = this$1.array_[i];
97383               if (e === o) {
97384                 return true
97385               }
97386             }
97387             return false
97388           };
97389
97390           /**
97391            * @override
97392            */
97393           HashSet.prototype.add = function add (o) {
97394             if (this.contains(o)) {
97395               return false
97396             }
97397
97398             this.array_.push(o);
97399
97400             return true
97401           };
97402
97403           /**
97404            * @override
97405            */
97406           HashSet.prototype.addAll = function addAll (c) {
97407             var this$1 = this;
97408
97409             for (var i = c.iterator(); i.hasNext();) {
97410               this$1.add(i.next());
97411             }
97412             return true
97413           };
97414
97415           /**
97416            * @override
97417            */
97418           HashSet.prototype.remove = function remove (o) {
97419             // throw new javascript.util.OperationNotSupported()
97420             throw new Error()
97421           };
97422
97423           /**
97424            * @override
97425            */
97426           HashSet.prototype.size = function size () {
97427             return this.array_.length
97428           };
97429
97430           /**
97431            * @override
97432            */
97433           HashSet.prototype.isEmpty = function isEmpty () {
97434             return this.array_.length === 0
97435           };
97436
97437           /**
97438            * @override
97439            */
97440           HashSet.prototype.toArray = function toArray () {
97441             var this$1 = this;
97442
97443             var array = [];
97444
97445             for (var i = 0, len = this.array_.length; i < len; i++) {
97446               array.push(this$1.array_[i]);
97447             }
97448
97449             return array
97450           };
97451
97452           /**
97453            * @override
97454            */
97455           HashSet.prototype.iterator = function iterator () {
97456             return new Iterator_$1(this)
97457           };
97458
97459           return HashSet;
97460         }(Set$2));
97461
97462         /**
97463            * @extends {Iterator}
97464            * @param {HashSet} hashSet
97465            * @constructor
97466            * @private
97467            */
97468         var Iterator_$1 = (function (Iterator$$1) {
97469           function Iterator_ (hashSet) {
97470             Iterator$$1.call(this);
97471             /**
97472              * @type {HashSet}
97473              * @private
97474              */
97475             this.hashSet_ = hashSet;
97476             /**
97477              * @type {number}
97478              * @private
97479              */
97480             this.position_ = 0;
97481           }
97482
97483           if ( Iterator$$1 ) Iterator_.__proto__ = Iterator$$1;
97484           Iterator_.prototype = Object.create( Iterator$$1 && Iterator$$1.prototype );
97485           Iterator_.prototype.constructor = Iterator_;
97486
97487           /**
97488            * @override
97489            */
97490           Iterator_.prototype.next = function next () {
97491             if (this.position_ === this.hashSet_.size()) {
97492               throw new NoSuchElementException()
97493             }
97494             return this.hashSet_.array_[this.position_++]
97495           };
97496
97497           /**
97498            * @override
97499            */
97500           Iterator_.prototype.hasNext = function hasNext () {
97501             if (this.position_ < this.hashSet_.size()) {
97502               return true
97503             } else {
97504               return false
97505             }
97506           };
97507
97508           /**
97509            * @override
97510            */
97511           Iterator_.prototype.remove = function remove () {
97512             throw new OperationNotSupported()
97513           };
97514
97515           return Iterator_;
97516         }(Iterator$1));
97517
97518         var BLACK = 0;
97519         var RED = 1;
97520         function colorOf (p) { return (p === null ? BLACK : p.color) }
97521         function parentOf (p) { return (p === null ? null : p.parent) }
97522         function setColor (p, c) { if (p !== null) { p.color = c; } }
97523         function leftOf (p) { return (p === null ? null : p.left) }
97524         function rightOf (p) { return (p === null ? null : p.right) }
97525
97526         /**
97527          * @see http://download.oracle.com/javase/6/docs/api/java/util/TreeMap.html
97528          *
97529          * @extends {SortedMap}
97530          * @constructor
97531          * @private
97532          */
97533         function TreeMap () {
97534           /**
97535            * @type {Object}
97536            * @private
97537            */
97538           this.root_ = null;
97539           /**
97540            * @type {number}
97541            * @private
97542           */
97543           this.size_ = 0;
97544         }
97545         TreeMap.prototype = new SortedMap();
97546
97547         /**
97548          * @override
97549          */
97550         TreeMap.prototype.get = function (key) {
97551           var p = this.root_;
97552           while (p !== null) {
97553             var cmp = key['compareTo'](p.key);
97554             if (cmp < 0) { p = p.left; }
97555             else if (cmp > 0) { p = p.right; }
97556             else { return p.value }
97557           }
97558           return null
97559         };
97560
97561         /**
97562          * @override
97563          */
97564         TreeMap.prototype.put = function (key, value) {
97565           if (this.root_ === null) {
97566             this.root_ = {
97567               key: key,
97568               value: value,
97569               left: null,
97570               right: null,
97571               parent: null,
97572               color: BLACK,
97573               getValue: function getValue () { return this.value },
97574               getKey: function getKey () { return this.key }
97575             };
97576             this.size_ = 1;
97577             return null
97578           }
97579           var t = this.root_;
97580           var parent;
97581           var cmp;
97582           do {
97583             parent = t;
97584             cmp = key['compareTo'](t.key);
97585             if (cmp < 0) {
97586               t = t.left;
97587             } else if (cmp > 0) {
97588               t = t.right;
97589             } else {
97590               var oldValue = t.value;
97591               t.value = value;
97592               return oldValue
97593             }
97594           } while (t !== null)
97595           var e = {
97596             key: key,
97597             left: null,
97598             right: null,
97599             value: value,
97600             parent: parent,
97601             color: BLACK,
97602             getValue: function getValue () { return this.value },
97603             getKey: function getKey () { return this.key }
97604           };
97605           if (cmp < 0) {
97606             parent.left = e;
97607           } else {
97608             parent.right = e;
97609           }
97610           this.fixAfterInsertion(e);
97611           this.size_++;
97612           return null
97613         };
97614
97615         /**
97616          * @param {Object} x
97617          */
97618         TreeMap.prototype.fixAfterInsertion = function (x) {
97619           var this$1 = this;
97620
97621           x.color = RED;
97622           while (x != null && x !== this.root_ && x.parent.color === RED) {
97623             if (parentOf(x) === leftOf(parentOf(parentOf(x)))) {
97624               var y = rightOf(parentOf(parentOf(x)));
97625               if (colorOf(y) === RED) {
97626                 setColor(parentOf(x), BLACK);
97627                 setColor(y, BLACK);
97628                 setColor(parentOf(parentOf(x)), RED);
97629                 x = parentOf(parentOf(x));
97630               } else {
97631                 if (x === rightOf(parentOf(x))) {
97632                   x = parentOf(x);
97633                   this$1.rotateLeft(x);
97634                 }
97635                 setColor(parentOf(x), BLACK);
97636                 setColor(parentOf(parentOf(x)), RED);
97637                 this$1.rotateRight(parentOf(parentOf(x)));
97638               }
97639             } else {
97640               var y$1 = leftOf(parentOf(parentOf(x)));
97641               if (colorOf(y$1) === RED) {
97642                 setColor(parentOf(x), BLACK);
97643                 setColor(y$1, BLACK);
97644                 setColor(parentOf(parentOf(x)), RED);
97645                 x = parentOf(parentOf(x));
97646               } else {
97647                 if (x === leftOf(parentOf(x))) {
97648                   x = parentOf(x);
97649                   this$1.rotateRight(x);
97650                 }
97651                 setColor(parentOf(x), BLACK);
97652                 setColor(parentOf(parentOf(x)), RED);
97653                 this$1.rotateLeft(parentOf(parentOf(x)));
97654               }
97655             }
97656           }
97657           this.root_.color = BLACK;
97658         };
97659
97660         /**
97661          * @override
97662          */
97663         TreeMap.prototype.values = function () {
97664           var arrayList = new ArrayList();
97665           var p = this.getFirstEntry();
97666           if (p !== null) {
97667             arrayList.add(p.value);
97668             while ((p = TreeMap.successor(p)) !== null) {
97669               arrayList.add(p.value);
97670             }
97671           }
97672           return arrayList
97673         };
97674
97675         /**
97676          * @override
97677          */
97678         TreeMap.prototype.entrySet = function () {
97679           var hashSet = new HashSet();
97680           var p = this.getFirstEntry();
97681           if (p !== null) {
97682             hashSet.add(p);
97683             while ((p = TreeMap.successor(p)) !== null) {
97684               hashSet.add(p);
97685             }
97686           }
97687           return hashSet
97688         };
97689
97690         /**
97691          * @param {Object} p
97692          */
97693         TreeMap.prototype.rotateLeft = function (p) {
97694           if (p != null) {
97695             var r = p.right;
97696             p.right = r.left;
97697             if (r.left != null) { r.left.parent = p; }
97698             r.parent = p.parent;
97699             if (p.parent === null) { this.root_ = r; } else if (p.parent.left === p) { p.parent.left = r; } else { p.parent.right = r; }
97700             r.left = p;
97701             p.parent = r;
97702           }
97703         };
97704
97705         /**
97706          * @param {Object} p
97707          */
97708         TreeMap.prototype.rotateRight = function (p) {
97709           if (p != null) {
97710             var l = p.left;
97711             p.left = l.right;
97712             if (l.right != null) { l.right.parent = p; }
97713             l.parent = p.parent;
97714             if (p.parent === null) { this.root_ = l; } else if (p.parent.right === p) { p.parent.right = l; } else { p.parent.left = l; }
97715             l.right = p;
97716             p.parent = l;
97717           }
97718         };
97719
97720         /**
97721          * @return {Object}
97722          */
97723         TreeMap.prototype.getFirstEntry = function () {
97724           var p = this.root_;
97725           if (p != null) {
97726             while (p.left != null) {
97727               p = p.left;
97728             }
97729           }
97730           return p
97731         };
97732
97733         /**
97734          * @param {Object} t
97735          * @return {Object}
97736          * @private
97737          */
97738         TreeMap.successor = function (t) {
97739           if (t === null) { return null } else if (t.right !== null) {
97740             var p = t.right;
97741             while (p.left !== null) {
97742               p = p.left;
97743             }
97744             return p
97745           } else {
97746             var p$1 = t.parent;
97747             var ch = t;
97748             while (p$1 !== null && ch === p$1.right) {
97749               ch = p$1;
97750               p$1 = p$1.parent;
97751             }
97752             return p$1
97753           }
97754         };
97755
97756         /**
97757          * @override
97758          */
97759         TreeMap.prototype.size = function () {
97760           return this.size_
97761         };
97762
97763         var Lineal = function Lineal () {};
97764
97765         Lineal.prototype.interfaces_ = function interfaces_ () {
97766           return []
97767         };
97768         Lineal.prototype.getClass = function getClass () {
97769           return Lineal
97770         };
97771
97772         /**
97773          * @see http://download.oracle.com/javase/6/docs/api/java/util/SortedSet.html
97774          *
97775          * @extends {Set}
97776          * @constructor
97777          * @private
97778          */
97779         function SortedSet () {}
97780         SortedSet.prototype = new Set$2();
97781
97782         // import Iterator from './Iterator'
97783         /**
97784          * @see http://download.oracle.com/javase/6/docs/api/java/util/TreeSet.html
97785          *
97786          * @extends {SortedSet}
97787          * @constructor
97788          * @private
97789          */
97790         function TreeSet () {
97791           /**
97792            * @type {Array}
97793            * @private
97794           */
97795           this.array_ = [];
97796
97797           if (arguments[0] instanceof Collection) {
97798             this.addAll(arguments[0]);
97799           }
97800         }
97801         TreeSet.prototype = new SortedSet();
97802
97803         /**
97804          * @override
97805          */
97806         TreeSet.prototype.contains = function (o) {
97807           var this$1 = this;
97808
97809           for (var i = 0, len = this.array_.length; i < len; i++) {
97810             var e = this$1.array_[i];
97811             if (e['compareTo'](o) === 0) {
97812               return true
97813             }
97814           }
97815           return false
97816         };
97817
97818         /**
97819          * @override
97820          */
97821         TreeSet.prototype.add = function (o) {
97822           var this$1 = this;
97823
97824           if (this.contains(o)) {
97825             return false
97826           }
97827
97828           for (var i = 0, len = this.array_.length; i < len; i++) {
97829             var e = this$1.array_[i];
97830             if (e['compareTo'](o) === 1) {
97831               this$1.array_.splice(i, 0, o);
97832               return true
97833             }
97834           }
97835
97836           this.array_.push(o);
97837
97838           return true
97839         };
97840
97841         /**
97842          * @override
97843          */
97844         TreeSet.prototype.addAll = function (c) {
97845           var this$1 = this;
97846
97847           for (var i = c.iterator(); i.hasNext();) {
97848             this$1.add(i.next());
97849           }
97850           return true
97851         };
97852
97853         /**
97854          * @override
97855          */
97856         TreeSet.prototype.remove = function (e) {
97857           throw new OperationNotSupported()
97858         };
97859
97860         /**
97861          * @override
97862          */
97863         TreeSet.prototype.size = function () {
97864           return this.array_.length
97865         };
97866
97867         /**
97868          * @override
97869          */
97870         TreeSet.prototype.isEmpty = function () {
97871           return this.array_.length === 0
97872         };
97873
97874         /**
97875          * @override
97876          */
97877         TreeSet.prototype.toArray = function () {
97878           var this$1 = this;
97879
97880           var array = [];
97881
97882           for (var i = 0, len = this.array_.length; i < len; i++) {
97883             array.push(this$1.array_[i]);
97884           }
97885
97886           return array
97887         };
97888
97889         /**
97890          * @override
97891          */
97892         TreeSet.prototype.iterator = function () {
97893           return new Iterator_$2(this)
97894         };
97895
97896         /**
97897          * @extends {javascript.util.Iterator}
97898          * @param {javascript.util.TreeSet} treeSet
97899          * @constructor
97900          * @private
97901          */
97902         var Iterator_$2 = function (treeSet) {
97903           /**
97904            * @type {javascript.util.TreeSet}
97905            * @private
97906            */
97907           this.treeSet_ = treeSet;
97908           /**
97909            * @type {number}
97910            * @private
97911            */
97912           this.position_ = 0;
97913         };
97914
97915         /**
97916          * @override
97917          */
97918         Iterator_$2.prototype.next = function () {
97919           if (this.position_ === this.treeSet_.size()) {
97920             throw new NoSuchElementException()
97921           }
97922           return this.treeSet_.array_[this.position_++]
97923         };
97924
97925         /**
97926          * @override
97927          */
97928         Iterator_$2.prototype.hasNext = function () {
97929           if (this.position_ < this.treeSet_.size()) {
97930             return true
97931           } else {
97932             return false
97933           }
97934         };
97935
97936         /**
97937          * @override
97938          */
97939         Iterator_$2.prototype.remove = function () {
97940           throw new OperationNotSupported()
97941         };
97942
97943         /**
97944          * @see http://download.oracle.com/javase/6/docs/api/java/util/Arrays.html
97945          *
97946          * @constructor
97947          * @private
97948          */
97949         var Arrays = function Arrays () {};
97950
97951         Arrays.sort = function sort () {
97952           var a = arguments[0];
97953           var i;
97954           var t;
97955           var comparator;
97956           var compare;
97957           if (arguments.length === 1) {
97958             compare = function (a, b) {
97959               return a.compareTo(b)
97960             };
97961             a.sort(compare);
97962           } else if (arguments.length === 2) {
97963             comparator = arguments[1];
97964             compare = function (a, b) {
97965               return comparator['compare'](a, b)
97966             };
97967             a.sort(compare);
97968           } else if (arguments.length === 3) {
97969             t = a.slice(arguments[1], arguments[2]);
97970             t.sort();
97971             var r = a.slice(0, arguments[1]).concat(t, a.slice(arguments[2], a.length));
97972             a.splice(0, a.length);
97973             for (i = 0; i < r.length; i++) {
97974               a.push(r[i]);
97975             }
97976           } else if (arguments.length === 4) {
97977             t = a.slice(arguments[1], arguments[2]);
97978             comparator = arguments[3];
97979             compare = function (a, b) {
97980               return comparator['compare'](a, b)
97981             };
97982             t.sort(compare);
97983             r = a.slice(0, arguments[1]).concat(t, a.slice(arguments[2], a.length));
97984             a.splice(0, a.length);
97985             for (i = 0; i < r.length; i++) {
97986               a.push(r[i]);
97987             }
97988           }
97989         };
97990         /**
97991          * @param {Array} array
97992          * @return {ArrayList}
97993          */
97994         Arrays.asList = function asList (array) {
97995           var arrayList = new ArrayList();
97996           for (var i = 0, len = array.length; i < len; i++) {
97997             arrayList.add(array[i]);
97998           }
97999           return arrayList
98000         };
98001
98002         var Dimension = function Dimension () {};
98003
98004         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 } };
98005
98006         staticAccessors$14.P.get = function () { return 0 };
98007         staticAccessors$14.L.get = function () { return 1 };
98008         staticAccessors$14.A.get = function () { return 2 };
98009         staticAccessors$14.FALSE.get = function () { return -1 };
98010         staticAccessors$14.TRUE.get = function () { return -2 };
98011         staticAccessors$14.DONTCARE.get = function () { return -3 };
98012         staticAccessors$14.SYM_FALSE.get = function () { return 'F' };
98013         staticAccessors$14.SYM_TRUE.get = function () { return 'T' };
98014         staticAccessors$14.SYM_DONTCARE.get = function () { return '*' };
98015         staticAccessors$14.SYM_P.get = function () { return '0' };
98016         staticAccessors$14.SYM_L.get = function () { return '1' };
98017         staticAccessors$14.SYM_A.get = function () { return '2' };
98018
98019         Dimension.prototype.interfaces_ = function interfaces_ () {
98020           return []
98021         };
98022         Dimension.prototype.getClass = function getClass () {
98023           return Dimension
98024         };
98025         Dimension.toDimensionSymbol = function toDimensionSymbol (dimensionValue) {
98026           switch (dimensionValue) {
98027             case Dimension.FALSE:
98028               return Dimension.SYM_FALSE
98029             case Dimension.TRUE:
98030               return Dimension.SYM_TRUE
98031             case Dimension.DONTCARE:
98032               return Dimension.SYM_DONTCARE
98033             case Dimension.P:
98034               return Dimension.SYM_P
98035             case Dimension.L:
98036               return Dimension.SYM_L
98037             case Dimension.A:
98038               return Dimension.SYM_A
98039           }
98040           throw new IllegalArgumentException('Unknown dimension value: ' + dimensionValue)
98041         };
98042         Dimension.toDimensionValue = function toDimensionValue (dimensionSymbol) {
98043           switch (Character.toUpperCase(dimensionSymbol)) {
98044             case Dimension.SYM_FALSE:
98045               return Dimension.FALSE
98046             case Dimension.SYM_TRUE:
98047               return Dimension.TRUE
98048             case Dimension.SYM_DONTCARE:
98049               return Dimension.DONTCARE
98050             case Dimension.SYM_P:
98051               return Dimension.P
98052             case Dimension.SYM_L:
98053               return Dimension.L
98054             case Dimension.SYM_A:
98055               return Dimension.A
98056           }
98057           throw new IllegalArgumentException('Unknown dimension symbol: ' + dimensionSymbol)
98058         };
98059
98060         Object.defineProperties( Dimension, staticAccessors$14 );
98061
98062         var GeometryFilter = function GeometryFilter () {};
98063
98064         GeometryFilter.prototype.filter = function filter (geom) {};
98065         GeometryFilter.prototype.interfaces_ = function interfaces_ () {
98066           return []
98067         };
98068         GeometryFilter.prototype.getClass = function getClass () {
98069           return GeometryFilter
98070         };
98071
98072         var CoordinateSequenceFilter = function CoordinateSequenceFilter () {};
98073
98074         CoordinateSequenceFilter.prototype.filter = function filter (seq, i) {};
98075         CoordinateSequenceFilter.prototype.isDone = function isDone () {};
98076         CoordinateSequenceFilter.prototype.isGeometryChanged = function isGeometryChanged () {};
98077         CoordinateSequenceFilter.prototype.interfaces_ = function interfaces_ () {
98078           return []
98079         };
98080         CoordinateSequenceFilter.prototype.getClass = function getClass () {
98081           return CoordinateSequenceFilter
98082         };
98083
98084         var GeometryCollection = (function (Geometry$$1) {
98085           function GeometryCollection (geometries, factory) {
98086             Geometry$$1.call(this, factory);
98087             this._geometries = geometries || [];
98088
98089             if (Geometry$$1.hasNullElements(this._geometries)) {
98090               throw new IllegalArgumentException('geometries must not contain null elements')
98091             }
98092           }
98093
98094           if ( Geometry$$1 ) GeometryCollection.__proto__ = Geometry$$1;
98095           GeometryCollection.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
98096           GeometryCollection.prototype.constructor = GeometryCollection;
98097
98098           var staticAccessors = { serialVersionUID: { configurable: true } };
98099           GeometryCollection.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
98100             var this$1 = this;
98101
98102             var envelope = new Envelope();
98103             for (var i = 0; i < this._geometries.length; i++) {
98104               envelope.expandToInclude(this$1._geometries[i].getEnvelopeInternal());
98105             }
98106             return envelope
98107           };
98108           GeometryCollection.prototype.getGeometryN = function getGeometryN (n) {
98109             return this._geometries[n]
98110           };
98111           GeometryCollection.prototype.getSortIndex = function getSortIndex () {
98112             return Geometry$$1.SORTINDEX_GEOMETRYCOLLECTION
98113           };
98114           GeometryCollection.prototype.getCoordinates = function getCoordinates () {
98115             var this$1 = this;
98116
98117             var coordinates = new Array(this.getNumPoints()).fill(null);
98118             var k = -1;
98119             for (var i = 0; i < this._geometries.length; i++) {
98120               var childCoordinates = this$1._geometries[i].getCoordinates();
98121               for (var j = 0; j < childCoordinates.length; j++) {
98122                 k++;
98123                 coordinates[k] = childCoordinates[j];
98124               }
98125             }
98126             return coordinates
98127           };
98128           GeometryCollection.prototype.getArea = function getArea () {
98129             var this$1 = this;
98130
98131             var area = 0.0;
98132             for (var i = 0; i < this._geometries.length; i++) {
98133               area += this$1._geometries[i].getArea();
98134             }
98135             return area
98136           };
98137           GeometryCollection.prototype.equalsExact = function equalsExact () {
98138             var this$1 = this;
98139
98140             if (arguments.length === 2) {
98141               var other = arguments[0];
98142               var tolerance = arguments[1];
98143               if (!this.isEquivalentClass(other)) {
98144                 return false
98145               }
98146               var otherCollection = other;
98147               if (this._geometries.length !== otherCollection._geometries.length) {
98148                 return false
98149               }
98150               for (var i = 0; i < this._geometries.length; i++) {
98151                 if (!this$1._geometries[i].equalsExact(otherCollection._geometries[i], tolerance)) {
98152                   return false
98153                 }
98154               }
98155               return true
98156             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
98157           };
98158           GeometryCollection.prototype.normalize = function normalize () {
98159             var this$1 = this;
98160
98161             for (var i = 0; i < this._geometries.length; i++) {
98162               this$1._geometries[i].normalize();
98163             }
98164             Arrays.sort(this._geometries);
98165           };
98166           GeometryCollection.prototype.getCoordinate = function getCoordinate () {
98167             if (this.isEmpty()) { return null }
98168             return this._geometries[0].getCoordinate()
98169           };
98170           GeometryCollection.prototype.getBoundaryDimension = function getBoundaryDimension () {
98171             var this$1 = this;
98172
98173             var dimension = Dimension.FALSE;
98174             for (var i = 0; i < this._geometries.length; i++) {
98175               dimension = Math.max(dimension, this$1._geometries[i].getBoundaryDimension());
98176             }
98177             return dimension
98178           };
98179           GeometryCollection.prototype.getDimension = function getDimension () {
98180             var this$1 = this;
98181
98182             var dimension = Dimension.FALSE;
98183             for (var i = 0; i < this._geometries.length; i++) {
98184               dimension = Math.max(dimension, this$1._geometries[i].getDimension());
98185             }
98186             return dimension
98187           };
98188           GeometryCollection.prototype.getLength = function getLength () {
98189             var this$1 = this;
98190
98191             var sum = 0.0;
98192             for (var i = 0; i < this._geometries.length; i++) {
98193               sum += this$1._geometries[i].getLength();
98194             }
98195             return sum
98196           };
98197           GeometryCollection.prototype.getNumPoints = function getNumPoints () {
98198             var this$1 = this;
98199
98200             var numPoints = 0;
98201             for (var i = 0; i < this._geometries.length; i++) {
98202               numPoints += this$1._geometries[i].getNumPoints();
98203             }
98204             return numPoints
98205           };
98206           GeometryCollection.prototype.getNumGeometries = function getNumGeometries () {
98207             return this._geometries.length
98208           };
98209           GeometryCollection.prototype.reverse = function reverse () {
98210             var this$1 = this;
98211
98212             var n = this._geometries.length;
98213             var revGeoms = new Array(n).fill(null);
98214             for (var i = 0; i < this._geometries.length; i++) {
98215               revGeoms[i] = this$1._geometries[i].reverse();
98216             }
98217             return this.getFactory().createGeometryCollection(revGeoms)
98218           };
98219           GeometryCollection.prototype.compareToSameClass = function compareToSameClass () {
98220             var this$1 = this;
98221
98222             if (arguments.length === 1) {
98223               var o = arguments[0];
98224               var theseElements = new TreeSet(Arrays.asList(this._geometries));
98225               var otherElements = new TreeSet(Arrays.asList(o._geometries));
98226               return this.compare(theseElements, otherElements)
98227             } else if (arguments.length === 2) {
98228               var o$1 = arguments[0];
98229               var comp = arguments[1];
98230               var gc = o$1;
98231               var n1 = this.getNumGeometries();
98232               var n2 = gc.getNumGeometries();
98233               var i = 0;
98234               while (i < n1 && i < n2) {
98235                 var thisGeom = this$1.getGeometryN(i);
98236                 var otherGeom = gc.getGeometryN(i);
98237                 var holeComp = thisGeom.compareToSameClass(otherGeom, comp);
98238                 if (holeComp !== 0) { return holeComp }
98239                 i++;
98240               }
98241               if (i < n1) { return 1 }
98242               if (i < n2) { return -1 }
98243               return 0
98244             }
98245           };
98246           GeometryCollection.prototype.apply = function apply () {
98247             var this$1 = this;
98248
98249             if (hasInterface(arguments[0], CoordinateFilter)) {
98250               var filter = arguments[0];
98251               for (var i = 0; i < this._geometries.length; i++) {
98252                 this$1._geometries[i].apply(filter);
98253               }
98254             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
98255               var filter$1 = arguments[0];
98256               if (this._geometries.length === 0) { return null }
98257               for (var i$1 = 0; i$1 < this._geometries.length; i$1++) {
98258                 this$1._geometries[i$1].apply(filter$1);
98259                 if (filter$1.isDone()) {
98260                   break
98261                 }
98262               }
98263               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
98264             } else if (hasInterface(arguments[0], GeometryFilter)) {
98265               var filter$2 = arguments[0];
98266               filter$2.filter(this);
98267               for (var i$2 = 0; i$2 < this._geometries.length; i$2++) {
98268                 this$1._geometries[i$2].apply(filter$2);
98269               }
98270             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
98271               var filter$3 = arguments[0];
98272               filter$3.filter(this);
98273               for (var i$3 = 0; i$3 < this._geometries.length; i$3++) {
98274                 this$1._geometries[i$3].apply(filter$3);
98275               }
98276             }
98277           };
98278           GeometryCollection.prototype.getBoundary = function getBoundary () {
98279             this.checkNotGeometryCollection(this);
98280             Assert.shouldNeverReachHere();
98281             return null
98282           };
98283           GeometryCollection.prototype.clone = function clone () {
98284             var this$1 = this;
98285
98286             var gc = Geometry$$1.prototype.clone.call(this);
98287             gc._geometries = new Array(this._geometries.length).fill(null);
98288             for (var i = 0; i < this._geometries.length; i++) {
98289               gc._geometries[i] = this$1._geometries[i].clone();
98290             }
98291             return gc
98292           };
98293           GeometryCollection.prototype.getGeometryType = function getGeometryType () {
98294             return 'GeometryCollection'
98295           };
98296           GeometryCollection.prototype.copy = function copy () {
98297             var this$1 = this;
98298
98299             var geometries = new Array(this._geometries.length).fill(null);
98300             for (var i = 0; i < geometries.length; i++) {
98301               geometries[i] = this$1._geometries[i].copy();
98302             }
98303             return new GeometryCollection(geometries, this._factory)
98304           };
98305           GeometryCollection.prototype.isEmpty = function isEmpty () {
98306             var this$1 = this;
98307
98308             for (var i = 0; i < this._geometries.length; i++) {
98309               if (!this$1._geometries[i].isEmpty()) {
98310                 return false
98311               }
98312             }
98313             return true
98314           };
98315           GeometryCollection.prototype.interfaces_ = function interfaces_ () {
98316             return []
98317           };
98318           GeometryCollection.prototype.getClass = function getClass () {
98319             return GeometryCollection
98320           };
98321           staticAccessors.serialVersionUID.get = function () { return -5694727726395021467 };
98322
98323           Object.defineProperties( GeometryCollection, staticAccessors );
98324
98325           return GeometryCollection;
98326         }(Geometry));
98327
98328         var MultiLineString = (function (GeometryCollection$$1) {
98329           function MultiLineString () {
98330             GeometryCollection$$1.apply(this, arguments);
98331           }
98332
98333           if ( GeometryCollection$$1 ) MultiLineString.__proto__ = GeometryCollection$$1;
98334           MultiLineString.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
98335           MultiLineString.prototype.constructor = MultiLineString;
98336
98337           var staticAccessors = { serialVersionUID: { configurable: true } };
98338
98339           MultiLineString.prototype.getSortIndex = function getSortIndex () {
98340             return Geometry.SORTINDEX_MULTILINESTRING
98341           };
98342           MultiLineString.prototype.equalsExact = function equalsExact () {
98343             if (arguments.length === 2) {
98344               var other = arguments[0];
98345               var tolerance = arguments[1];
98346               if (!this.isEquivalentClass(other)) {
98347                 return false
98348               }
98349               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
98350             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
98351           };
98352           MultiLineString.prototype.getBoundaryDimension = function getBoundaryDimension () {
98353             if (this.isClosed()) {
98354               return Dimension.FALSE
98355             }
98356             return 0
98357           };
98358           MultiLineString.prototype.isClosed = function isClosed () {
98359             var this$1 = this;
98360
98361             if (this.isEmpty()) {
98362               return false
98363             }
98364             for (var i = 0; i < this._geometries.length; i++) {
98365               if (!this$1._geometries[i].isClosed()) {
98366                 return false
98367               }
98368             }
98369             return true
98370           };
98371           MultiLineString.prototype.getDimension = function getDimension () {
98372             return 1
98373           };
98374           MultiLineString.prototype.reverse = function reverse () {
98375             var this$1 = this;
98376
98377             var nLines = this._geometries.length;
98378             var revLines = new Array(nLines).fill(null);
98379             for (var i = 0; i < this._geometries.length; i++) {
98380               revLines[nLines - 1 - i] = this$1._geometries[i].reverse();
98381             }
98382             return this.getFactory().createMultiLineString(revLines)
98383           };
98384           MultiLineString.prototype.getBoundary = function getBoundary () {
98385             return new BoundaryOp(this).getBoundary()
98386           };
98387           MultiLineString.prototype.getGeometryType = function getGeometryType () {
98388             return 'MultiLineString'
98389           };
98390           MultiLineString.prototype.copy = function copy () {
98391             var this$1 = this;
98392
98393             var lineStrings = new Array(this._geometries.length).fill(null);
98394             for (var i = 0; i < lineStrings.length; i++) {
98395               lineStrings[i] = this$1._geometries[i].copy();
98396             }
98397             return new MultiLineString(lineStrings, this._factory)
98398           };
98399           MultiLineString.prototype.interfaces_ = function interfaces_ () {
98400             return [Lineal]
98401           };
98402           MultiLineString.prototype.getClass = function getClass () {
98403             return MultiLineString
98404           };
98405           staticAccessors.serialVersionUID.get = function () { return 8166665132445433741 };
98406
98407           Object.defineProperties( MultiLineString, staticAccessors );
98408
98409           return MultiLineString;
98410         }(GeometryCollection));
98411
98412         var BoundaryOp = function BoundaryOp () {
98413           this._geom = null;
98414           this._geomFact = null;
98415           this._bnRule = null;
98416           this._endpointMap = null;
98417           if (arguments.length === 1) {
98418             var geom = arguments[0];
98419             var bnRule = BoundaryNodeRule.MOD2_BOUNDARY_RULE;
98420             this._geom = geom;
98421             this._geomFact = geom.getFactory();
98422             this._bnRule = bnRule;
98423           } else if (arguments.length === 2) {
98424             var geom$1 = arguments[0];
98425             var bnRule$1 = arguments[1];
98426             this._geom = geom$1;
98427             this._geomFact = geom$1.getFactory();
98428             this._bnRule = bnRule$1;
98429           }
98430         };
98431         BoundaryOp.prototype.boundaryMultiLineString = function boundaryMultiLineString (mLine) {
98432           if (this._geom.isEmpty()) {
98433             return this.getEmptyMultiPoint()
98434           }
98435           var bdyPts = this.computeBoundaryCoordinates(mLine);
98436           if (bdyPts.length === 1) {
98437             return this._geomFact.createPoint(bdyPts[0])
98438           }
98439           return this._geomFact.createMultiPointFromCoords(bdyPts)
98440         };
98441         BoundaryOp.prototype.getBoundary = function getBoundary () {
98442           if (this._geom instanceof LineString) { return this.boundaryLineString(this._geom) }
98443           if (this._geom instanceof MultiLineString) { return this.boundaryMultiLineString(this._geom) }
98444           return this._geom.getBoundary()
98445         };
98446         BoundaryOp.prototype.boundaryLineString = function boundaryLineString (line) {
98447           if (this._geom.isEmpty()) {
98448             return this.getEmptyMultiPoint()
98449           }
98450           if (line.isClosed()) {
98451             var closedEndpointOnBoundary = this._bnRule.isInBoundary(2);
98452             if (closedEndpointOnBoundary) {
98453               return line.getStartPoint()
98454             } else {
98455               return this._geomFact.createMultiPoint()
98456             }
98457           }
98458           return this._geomFact.createMultiPoint([line.getStartPoint(), line.getEndPoint()])
98459         };
98460         BoundaryOp.prototype.getEmptyMultiPoint = function getEmptyMultiPoint () {
98461           return this._geomFact.createMultiPoint()
98462         };
98463         BoundaryOp.prototype.computeBoundaryCoordinates = function computeBoundaryCoordinates (mLine) {
98464             var this$1 = this;
98465
98466           var bdyPts = new ArrayList();
98467           this._endpointMap = new TreeMap();
98468           for (var i = 0; i < mLine.getNumGeometries(); i++) {
98469             var line = mLine.getGeometryN(i);
98470             if (line.getNumPoints() === 0) { continue }
98471             this$1.addEndpoint(line.getCoordinateN(0));
98472             this$1.addEndpoint(line.getCoordinateN(line.getNumPoints() - 1));
98473           }
98474           for (var it = this._endpointMap.entrySet().iterator(); it.hasNext();) {
98475             var entry = it.next();
98476             var counter = entry.getValue();
98477             var valence = counter.count;
98478             if (this$1._bnRule.isInBoundary(valence)) {
98479               bdyPts.add(entry.getKey());
98480             }
98481           }
98482           return CoordinateArrays.toCoordinateArray(bdyPts)
98483         };
98484         BoundaryOp.prototype.addEndpoint = function addEndpoint (pt) {
98485           var counter = this._endpointMap.get(pt);
98486           if (counter === null) {
98487             counter = new Counter();
98488             this._endpointMap.put(pt, counter);
98489           }
98490           counter.count++;
98491         };
98492         BoundaryOp.prototype.interfaces_ = function interfaces_ () {
98493           return []
98494         };
98495         BoundaryOp.prototype.getClass = function getClass () {
98496           return BoundaryOp
98497         };
98498         BoundaryOp.getBoundary = function getBoundary () {
98499           if (arguments.length === 1) {
98500             var g = arguments[0];
98501             var bop = new BoundaryOp(g);
98502             return bop.getBoundary()
98503           } else if (arguments.length === 2) {
98504             var g$1 = arguments[0];
98505             var bnRule = arguments[1];
98506             var bop$1 = new BoundaryOp(g$1, bnRule);
98507             return bop$1.getBoundary()
98508           }
98509         };
98510
98511         var Counter = function Counter () {
98512           this.count = null;
98513         };
98514         Counter.prototype.interfaces_ = function interfaces_ () {
98515           return []
98516         };
98517         Counter.prototype.getClass = function getClass () {
98518           return Counter
98519         };
98520
98521         // boundary
98522
98523         function PrintStream () {}
98524
98525         function StringReader () {}
98526
98527         var DecimalFormat = function DecimalFormat () {};
98528
98529         function ByteArrayOutputStream () {}
98530
98531         function IOException () {}
98532
98533         function LineNumberReader () {}
98534
98535         var StringUtil = function StringUtil () {};
98536
98537         var staticAccessors$15 = { NEWLINE: { configurable: true },SIMPLE_ORDINATE_FORMAT: { configurable: true } };
98538
98539         StringUtil.prototype.interfaces_ = function interfaces_ () {
98540           return []
98541         };
98542         StringUtil.prototype.getClass = function getClass () {
98543           return StringUtil
98544         };
98545         StringUtil.chars = function chars (c, n) {
98546           var ch = new Array(n).fill(null);
98547           for (var i = 0; i < n; i++) {
98548             ch[i] = c;
98549           }
98550           return String(ch)
98551         };
98552         StringUtil.getStackTrace = function getStackTrace () {
98553           if (arguments.length === 1) {
98554             var t = arguments[0];
98555             var os = new ByteArrayOutputStream();
98556             var ps = new PrintStream(os);
98557             t.printStackTrace(ps);
98558             return os.toString()
98559           } else if (arguments.length === 2) {
98560             var t$1 = arguments[0];
98561             var depth = arguments[1];
98562             var stackTrace = '';
98563             var stringReader = new StringReader(StringUtil.getStackTrace(t$1));
98564             var lineNumberReader = new LineNumberReader(stringReader);
98565             for (var i = 0; i < depth; i++) {
98566               try {
98567                 stackTrace += lineNumberReader.readLine() + StringUtil.NEWLINE;
98568               } catch (e) {
98569                 if (e instanceof IOException) {
98570                   Assert.shouldNeverReachHere();
98571                 } else { throw e }
98572               } finally {}
98573             }
98574             return stackTrace
98575           }
98576         };
98577         StringUtil.split = function split (s, separator) {
98578           var separatorlen = separator.length;
98579           var tokenList = new ArrayList();
98580           var tmpString = '' + s;
98581           var pos = tmpString.indexOf(separator);
98582           while (pos >= 0) {
98583             var token = tmpString.substring(0, pos);
98584             tokenList.add(token);
98585             tmpString = tmpString.substring(pos + separatorlen);
98586             pos = tmpString.indexOf(separator);
98587           }
98588           if (tmpString.length > 0) { tokenList.add(tmpString); }
98589           var res = new Array(tokenList.size()).fill(null);
98590           for (var i = 0; i < res.length; i++) {
98591             res[i] = tokenList.get(i);
98592           }
98593           return res
98594         };
98595         StringUtil.toString = function toString () {
98596           if (arguments.length === 1) {
98597             var d = arguments[0];
98598             return StringUtil.SIMPLE_ORDINATE_FORMAT.format(d)
98599           }
98600         };
98601         StringUtil.spaces = function spaces (n) {
98602           return StringUtil.chars(' ', n)
98603         };
98604         staticAccessors$15.NEWLINE.get = function () { return System.getProperty('line.separator') };
98605         staticAccessors$15.SIMPLE_ORDINATE_FORMAT.get = function () { return new DecimalFormat('0.#') };
98606
98607         Object.defineProperties( StringUtil, staticAccessors$15 );
98608
98609         var CoordinateSequences = function CoordinateSequences () {};
98610
98611         CoordinateSequences.prototype.interfaces_ = function interfaces_ () {
98612           return []
98613         };
98614         CoordinateSequences.prototype.getClass = function getClass () {
98615           return CoordinateSequences
98616         };
98617         CoordinateSequences.copyCoord = function copyCoord (src, srcPos, dest, destPos) {
98618           var minDim = Math.min(src.getDimension(), dest.getDimension());
98619           for (var dim = 0; dim < minDim; dim++) {
98620             dest.setOrdinate(destPos, dim, src.getOrdinate(srcPos, dim));
98621           }
98622         };
98623         CoordinateSequences.isRing = function isRing (seq) {
98624           var n = seq.size();
98625           if (n === 0) { return true }
98626           if (n <= 3) { return false }
98627           return seq.getOrdinate(0, CoordinateSequence.X) === seq.getOrdinate(n - 1, CoordinateSequence.X) && seq.getOrdinate(0, CoordinateSequence.Y) === seq.getOrdinate(n - 1, CoordinateSequence.Y)
98628         };
98629         CoordinateSequences.isEqual = function isEqual (cs1, cs2) {
98630           var cs1Size = cs1.size();
98631           var cs2Size = cs2.size();
98632           if (cs1Size !== cs2Size) { return false }
98633           var dim = Math.min(cs1.getDimension(), cs2.getDimension());
98634           for (var i = 0; i < cs1Size; i++) {
98635             for (var d = 0; d < dim; d++) {
98636               var v1 = cs1.getOrdinate(i, d);
98637               var v2 = cs2.getOrdinate(i, d);
98638               if (cs1.getOrdinate(i, d) === cs2.getOrdinate(i, d)) { continue }
98639               if (Double.isNaN(v1) && Double.isNaN(v2)) { continue }
98640               return false
98641             }
98642           }
98643           return true
98644         };
98645         CoordinateSequences.extend = function extend (fact, seq, size) {
98646           var newseq = fact.create(size, seq.getDimension());
98647           var n = seq.size();
98648           CoordinateSequences.copy(seq, 0, newseq, 0, n);
98649           if (n > 0) {
98650             for (var i = n; i < size; i++) { CoordinateSequences.copy(seq, n - 1, newseq, i, 1); }
98651           }
98652           return newseq
98653         };
98654         CoordinateSequences.reverse = function reverse (seq) {
98655           var last = seq.size() - 1;
98656           var mid = Math.trunc(last / 2);
98657           for (var i = 0; i <= mid; i++) {
98658             CoordinateSequences.swap(seq, i, last - i);
98659           }
98660         };
98661         CoordinateSequences.swap = function swap (seq, i, j) {
98662           if (i === j) { return null }
98663           for (var dim = 0; dim < seq.getDimension(); dim++) {
98664             var tmp = seq.getOrdinate(i, dim);
98665             seq.setOrdinate(i, dim, seq.getOrdinate(j, dim));
98666             seq.setOrdinate(j, dim, tmp);
98667           }
98668         };
98669         CoordinateSequences.copy = function copy (src, srcPos, dest, destPos, length) {
98670           for (var i = 0; i < length; i++) {
98671             CoordinateSequences.copyCoord(src, srcPos + i, dest, destPos + i);
98672           }
98673         };
98674         CoordinateSequences.toString = function toString () {
98675           if (arguments.length === 1) {
98676             var cs = arguments[0];
98677             var size = cs.size();
98678             if (size === 0) { return '()' }
98679             var dim = cs.getDimension();
98680             var buf = new StringBuffer();
98681             buf.append('(');
98682             for (var i = 0; i < size; i++) {
98683               if (i > 0) { buf.append(' '); }
98684               for (var d = 0; d < dim; d++) {
98685                 if (d > 0) { buf.append(','); }
98686                 buf.append(StringUtil.toString(cs.getOrdinate(i, d)));
98687               }
98688             }
98689             buf.append(')');
98690             return buf.toString()
98691           }
98692         };
98693         CoordinateSequences.ensureValidRing = function ensureValidRing (fact, seq) {
98694           var n = seq.size();
98695           if (n === 0) { return seq }
98696           if (n <= 3) { return CoordinateSequences.createClosedRing(fact, seq, 4) }
98697           var isClosed = seq.getOrdinate(0, CoordinateSequence.X) === seq.getOrdinate(n - 1, CoordinateSequence.X) && seq.getOrdinate(0, CoordinateSequence.Y) === seq.getOrdinate(n - 1, CoordinateSequence.Y);
98698           if (isClosed) { return seq }
98699           return CoordinateSequences.createClosedRing(fact, seq, n + 1)
98700         };
98701         CoordinateSequences.createClosedRing = function createClosedRing (fact, seq, size) {
98702           var newseq = fact.create(size, seq.getDimension());
98703           var n = seq.size();
98704           CoordinateSequences.copy(seq, 0, newseq, 0, n);
98705           for (var i = n; i < size; i++) { CoordinateSequences.copy(seq, 0, newseq, i, 1); }
98706           return newseq
98707         };
98708
98709         var LineString = (function (Geometry$$1) {
98710           function LineString (points, factory) {
98711             Geometry$$1.call(this, factory);
98712             this._points = null;
98713             this.init(points);
98714           }
98715
98716           if ( Geometry$$1 ) LineString.__proto__ = Geometry$$1;
98717           LineString.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
98718           LineString.prototype.constructor = LineString;
98719
98720           var staticAccessors = { serialVersionUID: { configurable: true } };
98721           LineString.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
98722             if (this.isEmpty()) {
98723               return new Envelope()
98724             }
98725             return this._points.expandEnvelope(new Envelope())
98726           };
98727           LineString.prototype.isRing = function isRing () {
98728             return this.isClosed() && this.isSimple()
98729           };
98730           LineString.prototype.getSortIndex = function getSortIndex () {
98731             return Geometry$$1.SORTINDEX_LINESTRING
98732           };
98733           LineString.prototype.getCoordinates = function getCoordinates () {
98734             return this._points.toCoordinateArray()
98735           };
98736           LineString.prototype.equalsExact = function equalsExact () {
98737             var this$1 = this;
98738
98739             if (arguments.length === 2) {
98740               var other = arguments[0];
98741               var tolerance = arguments[1];
98742               if (!this.isEquivalentClass(other)) {
98743                 return false
98744               }
98745               var otherLineString = other;
98746               if (this._points.size() !== otherLineString._points.size()) {
98747                 return false
98748               }
98749               for (var i = 0; i < this._points.size(); i++) {
98750                 if (!this$1.equal(this$1._points.getCoordinate(i), otherLineString._points.getCoordinate(i), tolerance)) {
98751                   return false
98752                 }
98753               }
98754               return true
98755             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
98756           };
98757           LineString.prototype.normalize = function normalize () {
98758             var this$1 = this;
98759
98760             for (var i = 0; i < Math.trunc(this._points.size() / 2); i++) {
98761               var j = this$1._points.size() - 1 - i;
98762               if (!this$1._points.getCoordinate(i).equals(this$1._points.getCoordinate(j))) {
98763                 if (this$1._points.getCoordinate(i).compareTo(this$1._points.getCoordinate(j)) > 0) {
98764                   CoordinateSequences.reverse(this$1._points);
98765                 }
98766                 return null
98767               }
98768             }
98769           };
98770           LineString.prototype.getCoordinate = function getCoordinate () {
98771             if (this.isEmpty()) { return null }
98772             return this._points.getCoordinate(0)
98773           };
98774           LineString.prototype.getBoundaryDimension = function getBoundaryDimension () {
98775             if (this.isClosed()) {
98776               return Dimension.FALSE
98777             }
98778             return 0
98779           };
98780           LineString.prototype.isClosed = function isClosed () {
98781             if (this.isEmpty()) {
98782               return false
98783             }
98784             return this.getCoordinateN(0).equals2D(this.getCoordinateN(this.getNumPoints() - 1))
98785           };
98786           LineString.prototype.getEndPoint = function getEndPoint () {
98787             if (this.isEmpty()) {
98788               return null
98789             }
98790             return this.getPointN(this.getNumPoints() - 1)
98791           };
98792           LineString.prototype.getDimension = function getDimension () {
98793             return 1
98794           };
98795           LineString.prototype.getLength = function getLength () {
98796             return CGAlgorithms.computeLength(this._points)
98797           };
98798           LineString.prototype.getNumPoints = function getNumPoints () {
98799             return this._points.size()
98800           };
98801           LineString.prototype.reverse = function reverse () {
98802             var seq = this._points.copy();
98803             CoordinateSequences.reverse(seq);
98804             var revLine = this.getFactory().createLineString(seq);
98805             return revLine
98806           };
98807           LineString.prototype.compareToSameClass = function compareToSameClass () {
98808             var this$1 = this;
98809
98810             if (arguments.length === 1) {
98811               var o = arguments[0];
98812               var line = o;
98813               var i = 0;
98814               var j = 0;
98815               while (i < this._points.size() && j < line._points.size()) {
98816                 var comparison = this$1._points.getCoordinate(i).compareTo(line._points.getCoordinate(j));
98817                 if (comparison !== 0) {
98818                   return comparison
98819                 }
98820                 i++;
98821                 j++;
98822               }
98823               if (i < this._points.size()) {
98824                 return 1
98825               }
98826               if (j < line._points.size()) {
98827                 return -1
98828               }
98829               return 0
98830             } else if (arguments.length === 2) {
98831               var o$1 = arguments[0];
98832               var comp = arguments[1];
98833               var line$1 = o$1;
98834               return comp.compare(this._points, line$1._points)
98835             }
98836           };
98837           LineString.prototype.apply = function apply () {
98838             var this$1 = this;
98839
98840             if (hasInterface(arguments[0], CoordinateFilter)) {
98841               var filter = arguments[0];
98842               for (var i = 0; i < this._points.size(); i++) {
98843                 filter.filter(this$1._points.getCoordinate(i));
98844               }
98845             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
98846               var filter$1 = arguments[0];
98847               if (this._points.size() === 0) { return null }
98848               for (var i$1 = 0; i$1 < this._points.size(); i$1++) {
98849                 filter$1.filter(this$1._points, i$1);
98850                 if (filter$1.isDone()) { break }
98851               }
98852               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
98853             } else if (hasInterface(arguments[0], GeometryFilter)) {
98854               var filter$2 = arguments[0];
98855               filter$2.filter(this);
98856             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
98857               var filter$3 = arguments[0];
98858               filter$3.filter(this);
98859             }
98860           };
98861           LineString.prototype.getBoundary = function getBoundary () {
98862             return new BoundaryOp(this).getBoundary()
98863           };
98864           LineString.prototype.isEquivalentClass = function isEquivalentClass (other) {
98865             return other instanceof LineString
98866           };
98867           LineString.prototype.clone = function clone () {
98868             var ls = Geometry$$1.prototype.clone.call(this);
98869             ls._points = this._points.clone();
98870             return ls
98871           };
98872           LineString.prototype.getCoordinateN = function getCoordinateN (n) {
98873             return this._points.getCoordinate(n)
98874           };
98875           LineString.prototype.getGeometryType = function getGeometryType () {
98876             return 'LineString'
98877           };
98878           LineString.prototype.copy = function copy () {
98879             return new LineString(this._points.copy(), this._factory)
98880           };
98881           LineString.prototype.getCoordinateSequence = function getCoordinateSequence () {
98882             return this._points
98883           };
98884           LineString.prototype.isEmpty = function isEmpty () {
98885             return this._points.size() === 0
98886           };
98887           LineString.prototype.init = function init (points) {
98888             if (points === null) {
98889               points = this.getFactory().getCoordinateSequenceFactory().create([]);
98890             }
98891             if (points.size() === 1) {
98892               throw new IllegalArgumentException('Invalid number of points in LineString (found ' + points.size() + ' - must be 0 or >= 2)')
98893             }
98894             this._points = points;
98895           };
98896           LineString.prototype.isCoordinate = function isCoordinate (pt) {
98897             var this$1 = this;
98898
98899             for (var i = 0; i < this._points.size(); i++) {
98900               if (this$1._points.getCoordinate(i).equals(pt)) {
98901                 return true
98902               }
98903             }
98904             return false
98905           };
98906           LineString.prototype.getStartPoint = function getStartPoint () {
98907             if (this.isEmpty()) {
98908               return null
98909             }
98910             return this.getPointN(0)
98911           };
98912           LineString.prototype.getPointN = function getPointN (n) {
98913             return this.getFactory().createPoint(this._points.getCoordinate(n))
98914           };
98915           LineString.prototype.interfaces_ = function interfaces_ () {
98916             return [Lineal]
98917           };
98918           LineString.prototype.getClass = function getClass () {
98919             return LineString
98920           };
98921           staticAccessors.serialVersionUID.get = function () { return 3110669828065365560 };
98922
98923           Object.defineProperties( LineString, staticAccessors );
98924
98925           return LineString;
98926         }(Geometry));
98927
98928         var Puntal = function Puntal () {};
98929
98930         Puntal.prototype.interfaces_ = function interfaces_ () {
98931           return []
98932         };
98933         Puntal.prototype.getClass = function getClass () {
98934           return Puntal
98935         };
98936
98937         var Point$1 = (function (Geometry$$1) {
98938           function Point (coordinates, factory) {
98939             Geometry$$1.call(this, factory);
98940             this._coordinates = coordinates || null;
98941             this.init(this._coordinates);
98942           }
98943
98944           if ( Geometry$$1 ) Point.__proto__ = Geometry$$1;
98945           Point.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
98946           Point.prototype.constructor = Point;
98947
98948           var staticAccessors = { serialVersionUID: { configurable: true } };
98949           Point.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
98950             if (this.isEmpty()) {
98951               return new Envelope()
98952             }
98953             var env = new Envelope();
98954             env.expandToInclude(this._coordinates.getX(0), this._coordinates.getY(0));
98955             return env
98956           };
98957           Point.prototype.getSortIndex = function getSortIndex () {
98958             return Geometry$$1.SORTINDEX_POINT
98959           };
98960           Point.prototype.getCoordinates = function getCoordinates () {
98961             return this.isEmpty() ? [] : [this.getCoordinate()]
98962           };
98963           Point.prototype.equalsExact = function equalsExact () {
98964             if (arguments.length === 2) {
98965               var other = arguments[0];
98966               var tolerance = arguments[1];
98967               if (!this.isEquivalentClass(other)) {
98968                 return false
98969               }
98970               if (this.isEmpty() && other.isEmpty()) {
98971                 return true
98972               }
98973               if (this.isEmpty() !== other.isEmpty()) {
98974                 return false
98975               }
98976               return this.equal(other.getCoordinate(), this.getCoordinate(), tolerance)
98977             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
98978           };
98979           Point.prototype.normalize = function normalize () {};
98980           Point.prototype.getCoordinate = function getCoordinate () {
98981             return this._coordinates.size() !== 0 ? this._coordinates.getCoordinate(0) : null
98982           };
98983           Point.prototype.getBoundaryDimension = function getBoundaryDimension () {
98984             return Dimension.FALSE
98985           };
98986           Point.prototype.getDimension = function getDimension () {
98987             return 0
98988           };
98989           Point.prototype.getNumPoints = function getNumPoints () {
98990             return this.isEmpty() ? 0 : 1
98991           };
98992           Point.prototype.reverse = function reverse () {
98993             return this.copy()
98994           };
98995           Point.prototype.getX = function getX () {
98996             if (this.getCoordinate() === null) {
98997               throw new Error('getX called on empty Point')
98998             }
98999             return this.getCoordinate().x
99000           };
99001           Point.prototype.compareToSameClass = function compareToSameClass () {
99002             if (arguments.length === 1) {
99003               var other = arguments[0];
99004               var point$1 = other;
99005               return this.getCoordinate().compareTo(point$1.getCoordinate())
99006             } else if (arguments.length === 2) {
99007               var other$1 = arguments[0];
99008               var comp = arguments[1];
99009               var point = other$1;
99010               return comp.compare(this._coordinates, point._coordinates)
99011             }
99012           };
99013           Point.prototype.apply = function apply () {
99014             if (hasInterface(arguments[0], CoordinateFilter)) {
99015               var filter = arguments[0];
99016               if (this.isEmpty()) {
99017                 return null
99018               }
99019               filter.filter(this.getCoordinate());
99020             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
99021               var filter$1 = arguments[0];
99022               if (this.isEmpty()) { return null }
99023               filter$1.filter(this._coordinates, 0);
99024               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
99025             } else if (hasInterface(arguments[0], GeometryFilter)) {
99026               var filter$2 = arguments[0];
99027               filter$2.filter(this);
99028             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
99029               var filter$3 = arguments[0];
99030               filter$3.filter(this);
99031             }
99032           };
99033           Point.prototype.getBoundary = function getBoundary () {
99034             return this.getFactory().createGeometryCollection(null)
99035           };
99036           Point.prototype.clone = function clone () {
99037             var p = Geometry$$1.prototype.clone.call(this);
99038             p._coordinates = this._coordinates.clone();
99039             return p
99040           };
99041           Point.prototype.getGeometryType = function getGeometryType () {
99042             return 'Point'
99043           };
99044           Point.prototype.copy = function copy () {
99045             return new Point(this._coordinates.copy(), this._factory)
99046           };
99047           Point.prototype.getCoordinateSequence = function getCoordinateSequence () {
99048             return this._coordinates
99049           };
99050           Point.prototype.getY = function getY () {
99051             if (this.getCoordinate() === null) {
99052               throw new Error('getY called on empty Point')
99053             }
99054             return this.getCoordinate().y
99055           };
99056           Point.prototype.isEmpty = function isEmpty () {
99057             return this._coordinates.size() === 0
99058           };
99059           Point.prototype.init = function init (coordinates) {
99060             if (coordinates === null) {
99061               coordinates = this.getFactory().getCoordinateSequenceFactory().create([]);
99062             }
99063             Assert.isTrue(coordinates.size() <= 1);
99064             this._coordinates = coordinates;
99065           };
99066           Point.prototype.isSimple = function isSimple () {
99067             return true
99068           };
99069           Point.prototype.interfaces_ = function interfaces_ () {
99070             return [Puntal]
99071           };
99072           Point.prototype.getClass = function getClass () {
99073             return Point
99074           };
99075           staticAccessors.serialVersionUID.get = function () { return 4902022702746614570 };
99076
99077           Object.defineProperties( Point, staticAccessors );
99078
99079           return Point;
99080         }(Geometry));
99081
99082         var Polygonal = function Polygonal () {};
99083
99084         Polygonal.prototype.interfaces_ = function interfaces_ () {
99085           return []
99086         };
99087         Polygonal.prototype.getClass = function getClass () {
99088           return Polygonal
99089         };
99090
99091         var Polygon = (function (Geometry$$1) {
99092           function Polygon (shell, holes, factory) {
99093             Geometry$$1.call(this, factory);
99094             this._shell = null;
99095             this._holes = null;
99096             if (shell === null) {
99097               shell = this.getFactory().createLinearRing();
99098             }
99099             if (holes === null) {
99100               holes = [];
99101             }
99102             if (Geometry$$1.hasNullElements(holes)) {
99103               throw new IllegalArgumentException('holes must not contain null elements')
99104             }
99105             if (shell.isEmpty() && Geometry$$1.hasNonEmptyElements(holes)) {
99106               throw new IllegalArgumentException('shell is empty but holes are not')
99107             }
99108             this._shell = shell;
99109             this._holes = holes;
99110           }
99111
99112           if ( Geometry$$1 ) Polygon.__proto__ = Geometry$$1;
99113           Polygon.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
99114           Polygon.prototype.constructor = Polygon;
99115
99116           var staticAccessors = { serialVersionUID: { configurable: true } };
99117           Polygon.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
99118             return this._shell.getEnvelopeInternal()
99119           };
99120           Polygon.prototype.getSortIndex = function getSortIndex () {
99121             return Geometry$$1.SORTINDEX_POLYGON
99122           };
99123           Polygon.prototype.getCoordinates = function getCoordinates () {
99124             var this$1 = this;
99125
99126             if (this.isEmpty()) {
99127               return []
99128             }
99129             var coordinates = new Array(this.getNumPoints()).fill(null);
99130             var k = -1;
99131             var shellCoordinates = this._shell.getCoordinates();
99132             for (var x = 0; x < shellCoordinates.length; x++) {
99133               k++;
99134               coordinates[k] = shellCoordinates[x];
99135             }
99136             for (var i = 0; i < this._holes.length; i++) {
99137               var childCoordinates = this$1._holes[i].getCoordinates();
99138               for (var j = 0; j < childCoordinates.length; j++) {
99139                 k++;
99140                 coordinates[k] = childCoordinates[j];
99141               }
99142             }
99143             return coordinates
99144           };
99145           Polygon.prototype.getArea = function getArea () {
99146             var this$1 = this;
99147
99148             var area = 0.0;
99149             area += Math.abs(CGAlgorithms.signedArea(this._shell.getCoordinateSequence()));
99150             for (var i = 0; i < this._holes.length; i++) {
99151               area -= Math.abs(CGAlgorithms.signedArea(this$1._holes[i].getCoordinateSequence()));
99152             }
99153             return area
99154           };
99155           Polygon.prototype.isRectangle = function isRectangle () {
99156             if (this.getNumInteriorRing() !== 0) { return false }
99157             if (this._shell === null) { return false }
99158             if (this._shell.getNumPoints() !== 5) { return false }
99159             var seq = this._shell.getCoordinateSequence();
99160             var env = this.getEnvelopeInternal();
99161             for (var i = 0; i < 5; i++) {
99162               var x = seq.getX(i);
99163               if (!(x === env.getMinX() || x === env.getMaxX())) { return false }
99164               var y = seq.getY(i);
99165               if (!(y === env.getMinY() || y === env.getMaxY())) { return false }
99166             }
99167             var prevX = seq.getX(0);
99168             var prevY = seq.getY(0);
99169             for (var i$1 = 1; i$1 <= 4; i$1++) {
99170               var x$1 = seq.getX(i$1);
99171               var y$1 = seq.getY(i$1);
99172               var xChanged = x$1 !== prevX;
99173               var yChanged = y$1 !== prevY;
99174               if (xChanged === yChanged) { return false }
99175               prevX = x$1;
99176               prevY = y$1;
99177             }
99178             return true
99179           };
99180           Polygon.prototype.equalsExact = function equalsExact () {
99181             var this$1 = this;
99182
99183             if (arguments.length === 2) {
99184               var other = arguments[0];
99185               var tolerance = arguments[1];
99186               if (!this.isEquivalentClass(other)) {
99187                 return false
99188               }
99189               var otherPolygon = other;
99190               var thisShell = this._shell;
99191               var otherPolygonShell = otherPolygon._shell;
99192               if (!thisShell.equalsExact(otherPolygonShell, tolerance)) {
99193                 return false
99194               }
99195               if (this._holes.length !== otherPolygon._holes.length) {
99196                 return false
99197               }
99198               for (var i = 0; i < this._holes.length; i++) {
99199                 if (!this$1._holes[i].equalsExact(otherPolygon._holes[i], tolerance)) {
99200                   return false
99201                 }
99202               }
99203               return true
99204             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
99205           };
99206           Polygon.prototype.normalize = function normalize () {
99207             var this$1 = this;
99208
99209             if (arguments.length === 0) {
99210               this.normalize(this._shell, true);
99211               for (var i = 0; i < this._holes.length; i++) {
99212                 this$1.normalize(this$1._holes[i], false);
99213               }
99214               Arrays.sort(this._holes);
99215             } else if (arguments.length === 2) {
99216               var ring = arguments[0];
99217               var clockwise = arguments[1];
99218               if (ring.isEmpty()) {
99219                 return null
99220               }
99221               var uniqueCoordinates = new Array(ring.getCoordinates().length - 1).fill(null);
99222               System.arraycopy(ring.getCoordinates(), 0, uniqueCoordinates, 0, uniqueCoordinates.length);
99223               var minCoordinate = CoordinateArrays.minCoordinate(ring.getCoordinates());
99224               CoordinateArrays.scroll(uniqueCoordinates, minCoordinate);
99225               System.arraycopy(uniqueCoordinates, 0, ring.getCoordinates(), 0, uniqueCoordinates.length);
99226               ring.getCoordinates()[uniqueCoordinates.length] = uniqueCoordinates[0];
99227               if (CGAlgorithms.isCCW(ring.getCoordinates()) === clockwise) {
99228                 CoordinateArrays.reverse(ring.getCoordinates());
99229               }
99230             }
99231           };
99232           Polygon.prototype.getCoordinate = function getCoordinate () {
99233             return this._shell.getCoordinate()
99234           };
99235           Polygon.prototype.getNumInteriorRing = function getNumInteriorRing () {
99236             return this._holes.length
99237           };
99238           Polygon.prototype.getBoundaryDimension = function getBoundaryDimension () {
99239             return 1
99240           };
99241           Polygon.prototype.getDimension = function getDimension () {
99242             return 2
99243           };
99244           Polygon.prototype.getLength = function getLength () {
99245             var this$1 = this;
99246
99247             var len = 0.0;
99248             len += this._shell.getLength();
99249             for (var i = 0; i < this._holes.length; i++) {
99250               len += this$1._holes[i].getLength();
99251             }
99252             return len
99253           };
99254           Polygon.prototype.getNumPoints = function getNumPoints () {
99255             var this$1 = this;
99256
99257             var numPoints = this._shell.getNumPoints();
99258             for (var i = 0; i < this._holes.length; i++) {
99259               numPoints += this$1._holes[i].getNumPoints();
99260             }
99261             return numPoints
99262           };
99263           Polygon.prototype.reverse = function reverse () {
99264             var this$1 = this;
99265
99266             var poly = this.copy();
99267             poly._shell = this._shell.copy().reverse();
99268             poly._holes = new Array(this._holes.length).fill(null);
99269             for (var i = 0; i < this._holes.length; i++) {
99270               poly._holes[i] = this$1._holes[i].copy().reverse();
99271             }
99272             return poly
99273           };
99274           Polygon.prototype.convexHull = function convexHull () {
99275             return this.getExteriorRing().convexHull()
99276           };
99277           Polygon.prototype.compareToSameClass = function compareToSameClass () {
99278             var this$1 = this;
99279
99280             if (arguments.length === 1) {
99281               var o = arguments[0];
99282               var thisShell = this._shell;
99283               var otherShell = o._shell;
99284               return thisShell.compareToSameClass(otherShell)
99285             } else if (arguments.length === 2) {
99286               var o$1 = arguments[0];
99287               var comp = arguments[1];
99288               var poly = o$1;
99289               var thisShell$1 = this._shell;
99290               var otherShell$1 = poly._shell;
99291               var shellComp = thisShell$1.compareToSameClass(otherShell$1, comp);
99292               if (shellComp !== 0) { return shellComp }
99293               var nHole1 = this.getNumInteriorRing();
99294               var nHole2 = poly.getNumInteriorRing();
99295               var i = 0;
99296               while (i < nHole1 && i < nHole2) {
99297                 var thisHole = this$1.getInteriorRingN(i);
99298                 var otherHole = poly.getInteriorRingN(i);
99299                 var holeComp = thisHole.compareToSameClass(otherHole, comp);
99300                 if (holeComp !== 0) { return holeComp }
99301                 i++;
99302               }
99303               if (i < nHole1) { return 1 }
99304               if (i < nHole2) { return -1 }
99305               return 0
99306             }
99307           };
99308           Polygon.prototype.apply = function apply (filter) {
99309             var this$1 = this;
99310
99311             if (hasInterface(filter, CoordinateFilter)) {
99312               this._shell.apply(filter);
99313               for (var i$1 = 0; i$1 < this._holes.length; i$1++) {
99314                 this$1._holes[i$1].apply(filter);
99315               }
99316             } else if (hasInterface(filter, CoordinateSequenceFilter)) {
99317               this._shell.apply(filter);
99318               if (!filter.isDone()) {
99319                 for (var i$2 = 0; i$2 < this._holes.length; i$2++) {
99320                   this$1._holes[i$2].apply(filter);
99321                   if (filter.isDone()) { break }
99322                 }
99323               }
99324               if (filter.isGeometryChanged()) { this.geometryChanged(); }
99325             } else if (hasInterface(filter, GeometryFilter)) {
99326               filter.filter(this);
99327             } else if (hasInterface(filter, GeometryComponentFilter)) {
99328               filter.filter(this);
99329               this._shell.apply(filter);
99330               for (var i = 0; i < this._holes.length; i++) {
99331                 this$1._holes[i].apply(filter);
99332               }
99333             }
99334           };
99335           Polygon.prototype.getBoundary = function getBoundary () {
99336             var this$1 = this;
99337
99338             if (this.isEmpty()) {
99339               return this.getFactory().createMultiLineString()
99340             }
99341             var rings = new Array(this._holes.length + 1).fill(null);
99342             rings[0] = this._shell;
99343             for (var i = 0; i < this._holes.length; i++) {
99344               rings[i + 1] = this$1._holes[i];
99345             }
99346             if (rings.length <= 1) { return this.getFactory().createLinearRing(rings[0].getCoordinateSequence()) }
99347             return this.getFactory().createMultiLineString(rings)
99348           };
99349           Polygon.prototype.clone = function clone () {
99350             var this$1 = this;
99351
99352             var poly = Geometry$$1.prototype.clone.call(this);
99353             poly._shell = this._shell.clone();
99354             poly._holes = new Array(this._holes.length).fill(null);
99355             for (var i = 0; i < this._holes.length; i++) {
99356               poly._holes[i] = this$1._holes[i].clone();
99357             }
99358             return poly
99359           };
99360           Polygon.prototype.getGeometryType = function getGeometryType () {
99361             return 'Polygon'
99362           };
99363           Polygon.prototype.copy = function copy () {
99364             var this$1 = this;
99365
99366             var shell = this._shell.copy();
99367             var holes = new Array(this._holes.length).fill(null);
99368             for (var i = 0; i < holes.length; i++) {
99369               holes[i] = this$1._holes[i].copy();
99370             }
99371             return new Polygon(shell, holes, this._factory)
99372           };
99373           Polygon.prototype.getExteriorRing = function getExteriorRing () {
99374             return this._shell
99375           };
99376           Polygon.prototype.isEmpty = function isEmpty () {
99377             return this._shell.isEmpty()
99378           };
99379           Polygon.prototype.getInteriorRingN = function getInteriorRingN (n) {
99380             return this._holes[n]
99381           };
99382           Polygon.prototype.interfaces_ = function interfaces_ () {
99383             return [Polygonal]
99384           };
99385           Polygon.prototype.getClass = function getClass () {
99386             return Polygon
99387           };
99388           staticAccessors.serialVersionUID.get = function () { return -3494792200821764533 };
99389
99390           Object.defineProperties( Polygon, staticAccessors );
99391
99392           return Polygon;
99393         }(Geometry));
99394
99395         var MultiPoint = (function (GeometryCollection$$1) {
99396           function MultiPoint () {
99397             GeometryCollection$$1.apply(this, arguments);
99398           }
99399
99400           if ( GeometryCollection$$1 ) MultiPoint.__proto__ = GeometryCollection$$1;
99401           MultiPoint.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
99402           MultiPoint.prototype.constructor = MultiPoint;
99403
99404           var staticAccessors = { serialVersionUID: { configurable: true } };
99405
99406           MultiPoint.prototype.getSortIndex = function getSortIndex () {
99407             return Geometry.SORTINDEX_MULTIPOINT
99408           };
99409           MultiPoint.prototype.isValid = function isValid () {
99410             return true
99411           };
99412           MultiPoint.prototype.equalsExact = function equalsExact () {
99413             if (arguments.length === 2) {
99414               var other = arguments[0];
99415               var tolerance = arguments[1];
99416               if (!this.isEquivalentClass(other)) {
99417                 return false
99418               }
99419               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
99420             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
99421           };
99422           MultiPoint.prototype.getCoordinate = function getCoordinate () {
99423             if (arguments.length === 1) {
99424               var n = arguments[0];
99425               return this._geometries[n].getCoordinate()
99426             } else { return GeometryCollection$$1.prototype.getCoordinate.apply(this, arguments) }
99427           };
99428           MultiPoint.prototype.getBoundaryDimension = function getBoundaryDimension () {
99429             return Dimension.FALSE
99430           };
99431           MultiPoint.prototype.getDimension = function getDimension () {
99432             return 0
99433           };
99434           MultiPoint.prototype.getBoundary = function getBoundary () {
99435             return this.getFactory().createGeometryCollection(null)
99436           };
99437           MultiPoint.prototype.getGeometryType = function getGeometryType () {
99438             return 'MultiPoint'
99439           };
99440           MultiPoint.prototype.copy = function copy () {
99441             var this$1 = this;
99442
99443             var points = new Array(this._geometries.length).fill(null);
99444             for (var i = 0; i < points.length; i++) {
99445               points[i] = this$1._geometries[i].copy();
99446             }
99447             return new MultiPoint(points, this._factory)
99448           };
99449           MultiPoint.prototype.interfaces_ = function interfaces_ () {
99450             return [Puntal]
99451           };
99452           MultiPoint.prototype.getClass = function getClass () {
99453             return MultiPoint
99454           };
99455           staticAccessors.serialVersionUID.get = function () { return -8048474874175355449 };
99456
99457           Object.defineProperties( MultiPoint, staticAccessors );
99458
99459           return MultiPoint;
99460         }(GeometryCollection));
99461
99462         var LinearRing = (function (LineString$$1) {
99463           function LinearRing (points, factory) {
99464             if (points instanceof Coordinate && factory instanceof GeometryFactory) {
99465               points = factory.getCoordinateSequenceFactory().create(points);
99466             }
99467             LineString$$1.call(this, points, factory);
99468             this.validateConstruction();
99469           }
99470
99471           if ( LineString$$1 ) LinearRing.__proto__ = LineString$$1;
99472           LinearRing.prototype = Object.create( LineString$$1 && LineString$$1.prototype );
99473           LinearRing.prototype.constructor = LinearRing;
99474
99475           var staticAccessors = { MINIMUM_VALID_SIZE: { configurable: true },serialVersionUID: { configurable: true } };
99476           LinearRing.prototype.getSortIndex = function getSortIndex () {
99477             return Geometry.SORTINDEX_LINEARRING
99478           };
99479           LinearRing.prototype.getBoundaryDimension = function getBoundaryDimension () {
99480             return Dimension.FALSE
99481           };
99482           LinearRing.prototype.isClosed = function isClosed () {
99483             if (this.isEmpty()) {
99484               return true
99485             }
99486             return LineString$$1.prototype.isClosed.call(this)
99487           };
99488           LinearRing.prototype.reverse = function reverse () {
99489             var seq = this._points.copy();
99490             CoordinateSequences.reverse(seq);
99491             var rev = this.getFactory().createLinearRing(seq);
99492             return rev
99493           };
99494           LinearRing.prototype.validateConstruction = function validateConstruction () {
99495             if (!this.isEmpty() && !LineString$$1.prototype.isClosed.call(this)) {
99496               throw new IllegalArgumentException('Points of LinearRing do not form a closed linestring')
99497             }
99498             if (this.getCoordinateSequence().size() >= 1 && this.getCoordinateSequence().size() < LinearRing.MINIMUM_VALID_SIZE) {
99499               throw new IllegalArgumentException('Invalid number of points in LinearRing (found ' + this.getCoordinateSequence().size() + ' - must be 0 or >= 4)')
99500             }
99501           };
99502           LinearRing.prototype.getGeometryType = function getGeometryType () {
99503             return 'LinearRing'
99504           };
99505           LinearRing.prototype.copy = function copy () {
99506             return new LinearRing(this._points.copy(), this._factory)
99507           };
99508           LinearRing.prototype.interfaces_ = function interfaces_ () {
99509             return []
99510           };
99511           LinearRing.prototype.getClass = function getClass () {
99512             return LinearRing
99513           };
99514           staticAccessors.MINIMUM_VALID_SIZE.get = function () { return 4 };
99515           staticAccessors.serialVersionUID.get = function () { return -4261142084085851829 };
99516
99517           Object.defineProperties( LinearRing, staticAccessors );
99518
99519           return LinearRing;
99520         }(LineString));
99521
99522         var MultiPolygon = (function (GeometryCollection$$1) {
99523           function MultiPolygon () {
99524             GeometryCollection$$1.apply(this, arguments);
99525           }
99526
99527           if ( GeometryCollection$$1 ) MultiPolygon.__proto__ = GeometryCollection$$1;
99528           MultiPolygon.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
99529           MultiPolygon.prototype.constructor = MultiPolygon;
99530
99531           var staticAccessors = { serialVersionUID: { configurable: true } };
99532
99533           MultiPolygon.prototype.getSortIndex = function getSortIndex () {
99534             return Geometry.SORTINDEX_MULTIPOLYGON
99535           };
99536           MultiPolygon.prototype.equalsExact = function equalsExact () {
99537             if (arguments.length === 2) {
99538               var other = arguments[0];
99539               var tolerance = arguments[1];
99540               if (!this.isEquivalentClass(other)) {
99541                 return false
99542               }
99543               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
99544             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
99545           };
99546           MultiPolygon.prototype.getBoundaryDimension = function getBoundaryDimension () {
99547             return 1
99548           };
99549           MultiPolygon.prototype.getDimension = function getDimension () {
99550             return 2
99551           };
99552           MultiPolygon.prototype.reverse = function reverse () {
99553             var this$1 = this;
99554
99555             var n = this._geometries.length;
99556             var revGeoms = new Array(n).fill(null);
99557             for (var i = 0; i < this._geometries.length; i++) {
99558               revGeoms[i] = this$1._geometries[i].reverse();
99559             }
99560             return this.getFactory().createMultiPolygon(revGeoms)
99561           };
99562           MultiPolygon.prototype.getBoundary = function getBoundary () {
99563             var this$1 = this;
99564
99565             if (this.isEmpty()) {
99566               return this.getFactory().createMultiLineString()
99567             }
99568             var allRings = new ArrayList();
99569             for (var i = 0; i < this._geometries.length; i++) {
99570               var polygon = this$1._geometries[i];
99571               var rings = polygon.getBoundary();
99572               for (var j = 0; j < rings.getNumGeometries(); j++) {
99573                 allRings.add(rings.getGeometryN(j));
99574               }
99575             }
99576             var allRingsArray = new Array(allRings.size()).fill(null);
99577             return this.getFactory().createMultiLineString(allRings.toArray(allRingsArray))
99578           };
99579           MultiPolygon.prototype.getGeometryType = function getGeometryType () {
99580             return 'MultiPolygon'
99581           };
99582           MultiPolygon.prototype.copy = function copy () {
99583             var this$1 = this;
99584
99585             var polygons = new Array(this._geometries.length).fill(null);
99586             for (var i = 0; i < polygons.length; i++) {
99587               polygons[i] = this$1._geometries[i].copy();
99588             }
99589             return new MultiPolygon(polygons, this._factory)
99590           };
99591           MultiPolygon.prototype.interfaces_ = function interfaces_ () {
99592             return [Polygonal]
99593           };
99594           MultiPolygon.prototype.getClass = function getClass () {
99595             return MultiPolygon
99596           };
99597           staticAccessors.serialVersionUID.get = function () { return -551033529766975875 };
99598
99599           Object.defineProperties( MultiPolygon, staticAccessors );
99600
99601           return MultiPolygon;
99602         }(GeometryCollection));
99603
99604         var GeometryEditor = function GeometryEditor (factory) {
99605           this._factory = factory || null;
99606           this._isUserDataCopied = false;
99607         };
99608
99609         var staticAccessors$16 = { NoOpGeometryOperation: { configurable: true },CoordinateOperation: { configurable: true },CoordinateSequenceOperation: { configurable: true } };
99610         GeometryEditor.prototype.setCopyUserData = function setCopyUserData (isUserDataCopied) {
99611           this._isUserDataCopied = isUserDataCopied;
99612         };
99613         GeometryEditor.prototype.edit = function edit (geometry, operation) {
99614           if (geometry === null) { return null }
99615           var result = this.editInternal(geometry, operation);
99616           if (this._isUserDataCopied) {
99617             result.setUserData(geometry.getUserData());
99618           }
99619           return result
99620         };
99621         GeometryEditor.prototype.editInternal = function editInternal (geometry, operation) {
99622           if (this._factory === null) { this._factory = geometry.getFactory(); }
99623           if (geometry instanceof GeometryCollection) {
99624             return this.editGeometryCollection(geometry, operation)
99625           }
99626           if (geometry instanceof Polygon) {
99627             return this.editPolygon(geometry, operation)
99628           }
99629           if (geometry instanceof Point$1) {
99630             return operation.edit(geometry, this._factory)
99631           }
99632           if (geometry instanceof LineString) {
99633             return operation.edit(geometry, this._factory)
99634           }
99635           Assert.shouldNeverReachHere('Unsupported Geometry class: ' + geometry.getClass().getName());
99636           return null
99637         };
99638         GeometryEditor.prototype.editGeometryCollection = function editGeometryCollection (collection, operation) {
99639             var this$1 = this;
99640
99641           var collectionForType = operation.edit(collection, this._factory);
99642           var geometries = new ArrayList();
99643           for (var i = 0; i < collectionForType.getNumGeometries(); i++) {
99644             var geometry = this$1.edit(collectionForType.getGeometryN(i), operation);
99645             if (geometry === null || geometry.isEmpty()) {
99646               continue
99647             }
99648             geometries.add(geometry);
99649           }
99650           if (collectionForType.getClass() === MultiPoint) {
99651             return this._factory.createMultiPoint(geometries.toArray([]))
99652           }
99653           if (collectionForType.getClass() === MultiLineString) {
99654             return this._factory.createMultiLineString(geometries.toArray([]))
99655           }
99656           if (collectionForType.getClass() === MultiPolygon) {
99657             return this._factory.createMultiPolygon(geometries.toArray([]))
99658           }
99659           return this._factory.createGeometryCollection(geometries.toArray([]))
99660         };
99661         GeometryEditor.prototype.editPolygon = function editPolygon (polygon, operation) {
99662             var this$1 = this;
99663
99664           var newPolygon = operation.edit(polygon, this._factory);
99665           if (newPolygon === null) { newPolygon = this._factory.createPolygon(null); }
99666           if (newPolygon.isEmpty()) {
99667             return newPolygon
99668           }
99669           var shell = this.edit(newPolygon.getExteriorRing(), operation);
99670           if (shell === null || shell.isEmpty()) {
99671             return this._factory.createPolygon()
99672           }
99673           var holes = new ArrayList();
99674           for (var i = 0; i < newPolygon.getNumInteriorRing(); i++) {
99675             var hole = this$1.edit(newPolygon.getInteriorRingN(i), operation);
99676             if (hole === null || hole.isEmpty()) {
99677               continue
99678             }
99679             holes.add(hole);
99680           }
99681           return this._factory.createPolygon(shell, holes.toArray([]))
99682         };
99683         GeometryEditor.prototype.interfaces_ = function interfaces_ () {
99684           return []
99685         };
99686         GeometryEditor.prototype.getClass = function getClass () {
99687           return GeometryEditor
99688         };
99689         GeometryEditor.GeometryEditorOperation = function GeometryEditorOperation () {};
99690         staticAccessors$16.NoOpGeometryOperation.get = function () { return NoOpGeometryOperation };
99691         staticAccessors$16.CoordinateOperation.get = function () { return CoordinateOperation };
99692         staticAccessors$16.CoordinateSequenceOperation.get = function () { return CoordinateSequenceOperation };
99693
99694         Object.defineProperties( GeometryEditor, staticAccessors$16 );
99695
99696         var NoOpGeometryOperation = function NoOpGeometryOperation () {};
99697
99698         NoOpGeometryOperation.prototype.edit = function edit (geometry, factory) {
99699           return geometry
99700         };
99701         NoOpGeometryOperation.prototype.interfaces_ = function interfaces_ () {
99702           return [GeometryEditor.GeometryEditorOperation]
99703         };
99704         NoOpGeometryOperation.prototype.getClass = function getClass () {
99705           return NoOpGeometryOperation
99706         };
99707
99708         var CoordinateOperation = function CoordinateOperation () {};
99709
99710         CoordinateOperation.prototype.edit = function edit (geometry, factory) {
99711           var coords = this.editCoordinates(geometry.getCoordinates(), geometry);
99712           if (coords === null) { return geometry }
99713           if (geometry instanceof LinearRing) {
99714             return factory.createLinearRing(coords)
99715           }
99716           if (geometry instanceof LineString) {
99717             return factory.createLineString(coords)
99718           }
99719           if (geometry instanceof Point$1) {
99720             if (coords.length > 0) {
99721               return factory.createPoint(coords[0])
99722             } else {
99723               return factory.createPoint()
99724             }
99725           }
99726           return geometry
99727         };
99728         CoordinateOperation.prototype.interfaces_ = function interfaces_ () {
99729           return [GeometryEditor.GeometryEditorOperation]
99730         };
99731         CoordinateOperation.prototype.getClass = function getClass () {
99732           return CoordinateOperation
99733         };
99734
99735         var CoordinateSequenceOperation = function CoordinateSequenceOperation () {};
99736
99737         CoordinateSequenceOperation.prototype.edit = function edit (geometry, factory) {
99738           if (geometry instanceof LinearRing) {
99739             return factory.createLinearRing(this.edit(geometry.getCoordinateSequence(), geometry))
99740           }
99741           if (geometry instanceof LineString) {
99742             return factory.createLineString(this.edit(geometry.getCoordinateSequence(), geometry))
99743           }
99744           if (geometry instanceof Point$1) {
99745             return factory.createPoint(this.edit(geometry.getCoordinateSequence(), geometry))
99746           }
99747           return geometry
99748         };
99749         CoordinateSequenceOperation.prototype.interfaces_ = function interfaces_ () {
99750           return [GeometryEditor.GeometryEditorOperation]
99751         };
99752         CoordinateSequenceOperation.prototype.getClass = function getClass () {
99753           return CoordinateSequenceOperation
99754         };
99755
99756         var CoordinateArraySequence = function CoordinateArraySequence () {
99757           var this$1 = this;
99758
99759           this._dimension = 3;
99760           this._coordinates = null;
99761           if (arguments.length === 1) {
99762             if (arguments[0] instanceof Array) {
99763               this._coordinates = arguments[0];
99764               this._dimension = 3;
99765             } else if (Number.isInteger(arguments[0])) {
99766               var size = arguments[0];
99767               this._coordinates = new Array(size).fill(null);
99768               for (var i = 0; i < size; i++) {
99769                 this$1._coordinates[i] = new Coordinate();
99770               }
99771             } else if (hasInterface(arguments[0], CoordinateSequence)) {
99772               var coordSeq = arguments[0];
99773               if (coordSeq === null) {
99774                 this._coordinates = new Array(0).fill(null);
99775                 return null
99776               }
99777               this._dimension = coordSeq.getDimension();
99778               this._coordinates = new Array(coordSeq.size()).fill(null);
99779               for (var i$1 = 0; i$1 < this._coordinates.length; i$1++) {
99780                 this$1._coordinates[i$1] = coordSeq.getCoordinateCopy(i$1);
99781               }
99782             }
99783           } else if (arguments.length === 2) {
99784             if (arguments[0] instanceof Array && Number.isInteger(arguments[1])) {
99785               var coordinates = arguments[0];
99786               var dimension = arguments[1];
99787               this._coordinates = coordinates;
99788               this._dimension = dimension;
99789               if (coordinates === null) { this._coordinates = new Array(0).fill(null); }
99790             } else if (Number.isInteger(arguments[0]) && Number.isInteger(arguments[1])) {
99791               var size$1 = arguments[0];
99792               var dimension$1 = arguments[1];
99793               this._coordinates = new Array(size$1).fill(null);
99794               this._dimension = dimension$1;
99795               for (var i$2 = 0; i$2 < size$1; i$2++) {
99796                 this$1._coordinates[i$2] = new Coordinate();
99797               }
99798             }
99799           }
99800         };
99801
99802         var staticAccessors$18 = { serialVersionUID: { configurable: true } };
99803         CoordinateArraySequence.prototype.setOrdinate = function setOrdinate (index, ordinateIndex, value) {
99804           switch (ordinateIndex) {
99805             case CoordinateSequence.X:
99806               this._coordinates[index].x = value;
99807               break
99808             case CoordinateSequence.Y:
99809               this._coordinates[index].y = value;
99810               break
99811             case CoordinateSequence.Z:
99812               this._coordinates[index].z = value;
99813               break
99814             default:
99815               throw new IllegalArgumentException('invalid ordinateIndex')
99816           }
99817         };
99818         CoordinateArraySequence.prototype.size = function size () {
99819           return this._coordinates.length
99820         };
99821         CoordinateArraySequence.prototype.getOrdinate = function getOrdinate (index, ordinateIndex) {
99822           switch (ordinateIndex) {
99823             case CoordinateSequence.X:
99824               return this._coordinates[index].x
99825             case CoordinateSequence.Y:
99826               return this._coordinates[index].y
99827             case CoordinateSequence.Z:
99828               return this._coordinates[index].z
99829           }
99830           return Double.NaN
99831         };
99832         CoordinateArraySequence.prototype.getCoordinate = function getCoordinate () {
99833           if (arguments.length === 1) {
99834             var i = arguments[0];
99835             return this._coordinates[i]
99836           } else if (arguments.length === 2) {
99837             var index = arguments[0];
99838             var coord = arguments[1];
99839             coord.x = this._coordinates[index].x;
99840             coord.y = this._coordinates[index].y;
99841             coord.z = this._coordinates[index].z;
99842           }
99843         };
99844         CoordinateArraySequence.prototype.getCoordinateCopy = function getCoordinateCopy (i) {
99845           return new Coordinate(this._coordinates[i])
99846         };
99847         CoordinateArraySequence.prototype.getDimension = function getDimension () {
99848           return this._dimension
99849         };
99850         CoordinateArraySequence.prototype.getX = function getX (index) {
99851           return this._coordinates[index].x
99852         };
99853         CoordinateArraySequence.prototype.clone = function clone () {
99854             var this$1 = this;
99855
99856           var cloneCoordinates = new Array(this.size()).fill(null);
99857           for (var i = 0; i < this._coordinates.length; i++) {
99858             cloneCoordinates[i] = this$1._coordinates[i].clone();
99859           }
99860           return new CoordinateArraySequence(cloneCoordinates, this._dimension)
99861         };
99862         CoordinateArraySequence.prototype.expandEnvelope = function expandEnvelope (env) {
99863             var this$1 = this;
99864
99865           for (var i = 0; i < this._coordinates.length; i++) {
99866             env.expandToInclude(this$1._coordinates[i]);
99867           }
99868           return env
99869         };
99870         CoordinateArraySequence.prototype.copy = function copy () {
99871             var this$1 = this;
99872
99873           var cloneCoordinates = new Array(this.size()).fill(null);
99874           for (var i = 0; i < this._coordinates.length; i++) {
99875             cloneCoordinates[i] = this$1._coordinates[i].copy();
99876           }
99877           return new CoordinateArraySequence(cloneCoordinates, this._dimension)
99878         };
99879         CoordinateArraySequence.prototype.toString = function toString () {
99880             var this$1 = this;
99881
99882           if (this._coordinates.length > 0) {
99883             var strBuf = new StringBuffer(17 * this._coordinates.length);
99884             strBuf.append('(');
99885             strBuf.append(this._coordinates[0]);
99886             for (var i = 1; i < this._coordinates.length; i++) {
99887               strBuf.append(', ');
99888               strBuf.append(this$1._coordinates[i]);
99889             }
99890             strBuf.append(')');
99891             return strBuf.toString()
99892           } else {
99893             return '()'
99894           }
99895         };
99896         CoordinateArraySequence.prototype.getY = function getY (index) {
99897           return this._coordinates[index].y
99898         };
99899         CoordinateArraySequence.prototype.toCoordinateArray = function toCoordinateArray () {
99900           return this._coordinates
99901         };
99902         CoordinateArraySequence.prototype.interfaces_ = function interfaces_ () {
99903           return [CoordinateSequence, Serializable]
99904         };
99905         CoordinateArraySequence.prototype.getClass = function getClass () {
99906           return CoordinateArraySequence
99907         };
99908         staticAccessors$18.serialVersionUID.get = function () { return -915438501601840650 };
99909
99910         Object.defineProperties( CoordinateArraySequence, staticAccessors$18 );
99911
99912         var CoordinateArraySequenceFactory = function CoordinateArraySequenceFactory () {};
99913
99914         var staticAccessors$17 = { serialVersionUID: { configurable: true },instanceObject: { configurable: true } };
99915
99916         CoordinateArraySequenceFactory.prototype.readResolve = function readResolve () {
99917           return CoordinateArraySequenceFactory.instance()
99918         };
99919         CoordinateArraySequenceFactory.prototype.create = function create () {
99920           if (arguments.length === 1) {
99921             if (arguments[0] instanceof Array) {
99922               var coordinates = arguments[0];
99923               return new CoordinateArraySequence(coordinates)
99924             } else if (hasInterface(arguments[0], CoordinateSequence)) {
99925               var coordSeq = arguments[0];
99926               return new CoordinateArraySequence(coordSeq)
99927             }
99928           } else if (arguments.length === 2) {
99929             var size = arguments[0];
99930             var dimension = arguments[1];
99931             if (dimension > 3) { dimension = 3; }
99932             if (dimension < 2) { return new CoordinateArraySequence(size) }
99933             return new CoordinateArraySequence(size, dimension)
99934           }
99935         };
99936         CoordinateArraySequenceFactory.prototype.interfaces_ = function interfaces_ () {
99937           return [CoordinateSequenceFactory, Serializable]
99938         };
99939         CoordinateArraySequenceFactory.prototype.getClass = function getClass () {
99940           return CoordinateArraySequenceFactory
99941         };
99942         CoordinateArraySequenceFactory.instance = function instance () {
99943           return CoordinateArraySequenceFactory.instanceObject
99944         };
99945
99946         staticAccessors$17.serialVersionUID.get = function () { return -4099577099607551657 };
99947         staticAccessors$17.instanceObject.get = function () { return new CoordinateArraySequenceFactory() };
99948
99949         Object.defineProperties( CoordinateArraySequenceFactory, staticAccessors$17 );
99950
99951         /**
99952          * @see http://download.oracle.com/javase/6/docs/api/java/util/HashMap.html
99953          *
99954          * @extends {javascript.util.Map}
99955          * @constructor
99956          * @private
99957          */
99958         var HashMap = (function (MapInterface) {
99959           function HashMap () {
99960             MapInterface.call(this);
99961             this.map_ = new Map();
99962           }
99963
99964           if ( MapInterface ) HashMap.__proto__ = MapInterface;
99965           HashMap.prototype = Object.create( MapInterface && MapInterface.prototype );
99966           HashMap.prototype.constructor = HashMap;
99967           /**
99968            * @override
99969            */
99970           HashMap.prototype.get = function get (key) {
99971             return this.map_.get(key) || null
99972           };
99973
99974           /**
99975            * @override
99976            */
99977           HashMap.prototype.put = function put (key, value) {
99978             this.map_.set(key, value);
99979             return value
99980           };
99981
99982           /**
99983            * @override
99984            */
99985           HashMap.prototype.values = function values () {
99986             var arrayList = new ArrayList();
99987             var it = this.map_.values();
99988             var o = it.next();
99989             while (!o.done) {
99990               arrayList.add(o.value);
99991               o = it.next();
99992             }
99993             return arrayList
99994           };
99995
99996           /**
99997            * @override
99998            */
99999           HashMap.prototype.entrySet = function entrySet () {
100000             var hashSet = new HashSet();
100001             this.map_.entries().forEach(function (entry) { return hashSet.add(entry); });
100002             return hashSet
100003           };
100004
100005           /**
100006            * @override
100007            */
100008           HashMap.prototype.size = function size () {
100009             return this.map_.size()
100010           };
100011
100012           return HashMap;
100013         }(Map$1$1));
100014
100015         var PrecisionModel = function PrecisionModel () {
100016           this._modelType = null;
100017           this._scale = null;
100018           if (arguments.length === 0) {
100019             this._modelType = PrecisionModel.FLOATING;
100020           } else if (arguments.length === 1) {
100021             if (arguments[0] instanceof Type$2) {
100022               var modelType = arguments[0];
100023               this._modelType = modelType;
100024               if (modelType === PrecisionModel.FIXED) {
100025                 this.setScale(1.0);
100026               }
100027             } else if (typeof arguments[0] === 'number') {
100028               var scale = arguments[0];
100029               this._modelType = PrecisionModel.FIXED;
100030               this.setScale(scale);
100031             } else if (arguments[0] instanceof PrecisionModel) {
100032               var pm = arguments[0];
100033               this._modelType = pm._modelType;
100034               this._scale = pm._scale;
100035             }
100036           }
100037         };
100038
100039         var staticAccessors$19 = { serialVersionUID: { configurable: true },maximumPreciseValue: { configurable: true } };
100040         PrecisionModel.prototype.equals = function equals (other) {
100041           if (!(other instanceof PrecisionModel)) {
100042             return false
100043           }
100044           var otherPrecisionModel = other;
100045           return this._modelType === otherPrecisionModel._modelType && this._scale === otherPrecisionModel._scale
100046         };
100047         PrecisionModel.prototype.compareTo = function compareTo (o) {
100048           var other = o;
100049           var sigDigits = this.getMaximumSignificantDigits();
100050           var otherSigDigits = other.getMaximumSignificantDigits();
100051           return new Integer(sigDigits).compareTo(new Integer(otherSigDigits))
100052         };
100053         PrecisionModel.prototype.getScale = function getScale () {
100054           return this._scale
100055         };
100056         PrecisionModel.prototype.isFloating = function isFloating () {
100057           return this._modelType === PrecisionModel.FLOATING || this._modelType === PrecisionModel.FLOATING_SINGLE
100058         };
100059         PrecisionModel.prototype.getType = function getType () {
100060           return this._modelType
100061         };
100062         PrecisionModel.prototype.toString = function toString () {
100063           var description = 'UNKNOWN';
100064           if (this._modelType === PrecisionModel.FLOATING) {
100065             description = 'Floating';
100066           } else if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100067             description = 'Floating-Single';
100068           } else if (this._modelType === PrecisionModel.FIXED) {
100069             description = 'Fixed (Scale=' + this.getScale() + ')';
100070           }
100071           return description
100072         };
100073         PrecisionModel.prototype.makePrecise = function makePrecise () {
100074           if (typeof arguments[0] === 'number') {
100075             var val = arguments[0];
100076             if (Double.isNaN(val)) { return val }
100077             if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100078               var floatSingleVal = val;
100079               return floatSingleVal
100080             }
100081             if (this._modelType === PrecisionModel.FIXED) {
100082               return Math.round(val * this._scale) / this._scale
100083             }
100084             return val
100085           } else if (arguments[0] instanceof Coordinate) {
100086             var coord = arguments[0];
100087             if (this._modelType === PrecisionModel.FLOATING) { return null }
100088             coord.x = this.makePrecise(coord.x);
100089             coord.y = this.makePrecise(coord.y);
100090           }
100091         };
100092         PrecisionModel.prototype.getMaximumSignificantDigits = function getMaximumSignificantDigits () {
100093           var maxSigDigits = 16;
100094           if (this._modelType === PrecisionModel.FLOATING) {
100095             maxSigDigits = 16;
100096           } else if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100097             maxSigDigits = 6;
100098           } else if (this._modelType === PrecisionModel.FIXED) {
100099             maxSigDigits = 1 + Math.trunc(Math.ceil(Math.log(this.getScale()) / Math.log(10)));
100100           }
100101           return maxSigDigits
100102         };
100103         PrecisionModel.prototype.setScale = function setScale (scale) {
100104           this._scale = Math.abs(scale);
100105         };
100106         PrecisionModel.prototype.interfaces_ = function interfaces_ () {
100107           return [Serializable, Comparable]
100108         };
100109         PrecisionModel.prototype.getClass = function getClass () {
100110           return PrecisionModel
100111         };
100112         PrecisionModel.mostPrecise = function mostPrecise (pm1, pm2) {
100113           if (pm1.compareTo(pm2) >= 0) { return pm1 }
100114           return pm2
100115         };
100116         staticAccessors$19.serialVersionUID.get = function () { return 7777263578777803835 };
100117         staticAccessors$19.maximumPreciseValue.get = function () { return 9007199254740992.0 };
100118
100119         Object.defineProperties( PrecisionModel, staticAccessors$19 );
100120
100121         var Type$2 = function Type (name) {
100122           this._name = name || null;
100123           Type.nameToTypeMap.put(name, this);
100124         };
100125
100126         var staticAccessors$1$1 = { serialVersionUID: { configurable: true },nameToTypeMap: { configurable: true } };
100127         Type$2.prototype.readResolve = function readResolve () {
100128           return Type$2.nameToTypeMap.get(this._name)
100129         };
100130         Type$2.prototype.toString = function toString () {
100131           return this._name
100132         };
100133         Type$2.prototype.interfaces_ = function interfaces_ () {
100134           return [Serializable]
100135         };
100136         Type$2.prototype.getClass = function getClass () {
100137           return Type$2
100138         };
100139         staticAccessors$1$1.serialVersionUID.get = function () { return -5528602631731589822 };
100140         staticAccessors$1$1.nameToTypeMap.get = function () { return new HashMap() };
100141
100142         Object.defineProperties( Type$2, staticAccessors$1$1 );
100143
100144         PrecisionModel.Type = Type$2;
100145         PrecisionModel.FIXED = new Type$2('FIXED');
100146         PrecisionModel.FLOATING = new Type$2('FLOATING');
100147         PrecisionModel.FLOATING_SINGLE = new Type$2('FLOATING SINGLE');
100148
100149         var GeometryFactory = function GeometryFactory () {
100150           this._precisionModel = new PrecisionModel();
100151           this._SRID = 0;
100152           this._coordinateSequenceFactory = GeometryFactory.getDefaultCoordinateSequenceFactory();
100153
100154           if (arguments.length === 0) ; else if (arguments.length === 1) {
100155             if (hasInterface(arguments[0], CoordinateSequenceFactory)) {
100156               this._coordinateSequenceFactory = arguments[0];
100157             } else if (arguments[0] instanceof PrecisionModel) {
100158               this._precisionModel = arguments[0];
100159             }
100160           } else if (arguments.length === 2) {
100161             this._precisionModel = arguments[0];
100162             this._SRID = arguments[1];
100163           } else if (arguments.length === 3) {
100164             this._precisionModel = arguments[0];
100165             this._SRID = arguments[1];
100166             this._coordinateSequenceFactory = arguments[2];
100167           }
100168         };
100169
100170         var staticAccessors$2 = { serialVersionUID: { configurable: true } };
100171         GeometryFactory.prototype.toGeometry = function toGeometry (envelope) {
100172           if (envelope.isNull()) {
100173             return this.createPoint(null)
100174           }
100175           if (envelope.getMinX() === envelope.getMaxX() && envelope.getMinY() === envelope.getMaxY()) {
100176             return this.createPoint(new Coordinate(envelope.getMinX(), envelope.getMinY()))
100177           }
100178           if (envelope.getMinX() === envelope.getMaxX() || envelope.getMinY() === envelope.getMaxY()) {
100179             return this.createLineString([new Coordinate(envelope.getMinX(), envelope.getMinY()), new Coordinate(envelope.getMaxX(), envelope.getMaxY())])
100180           }
100181           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)
100182         };
100183         GeometryFactory.prototype.createLineString = function createLineString (coordinates) {
100184           if (!coordinates) { return new LineString(this.getCoordinateSequenceFactory().create([]), this) }
100185           else if (coordinates instanceof Array) { return new LineString(this.getCoordinateSequenceFactory().create(coordinates), this) }
100186           else if (hasInterface(coordinates, CoordinateSequence)) { return new LineString(coordinates, this) }
100187         };
100188         GeometryFactory.prototype.createMultiLineString = function createMultiLineString () {
100189           if (arguments.length === 0) {
100190             return new MultiLineString(null, this)
100191           } else if (arguments.length === 1) {
100192             var lineStrings = arguments[0];
100193             return new MultiLineString(lineStrings, this)
100194           }
100195         };
100196         GeometryFactory.prototype.buildGeometry = function buildGeometry (geomList) {
100197           var geomClass = null;
100198           var isHeterogeneous = false;
100199           var hasGeometryCollection = false;
100200           for (var i = geomList.iterator(); i.hasNext();) {
100201             var geom = i.next();
100202             var partClass = geom.getClass();
100203             if (geomClass === null) {
100204               geomClass = partClass;
100205             }
100206             if (partClass !== geomClass) {
100207               isHeterogeneous = true;
100208             }
100209             if (geom.isGeometryCollectionOrDerived()) { hasGeometryCollection = true; }
100210           }
100211           if (geomClass === null) {
100212             return this.createGeometryCollection()
100213           }
100214           if (isHeterogeneous || hasGeometryCollection) {
100215             return this.createGeometryCollection(GeometryFactory.toGeometryArray(geomList))
100216           }
100217           var geom0 = geomList.iterator().next();
100218           var isCollection = geomList.size() > 1;
100219           if (isCollection) {
100220             if (geom0 instanceof Polygon) {
100221               return this.createMultiPolygon(GeometryFactory.toPolygonArray(geomList))
100222             } else if (geom0 instanceof LineString) {
100223               return this.createMultiLineString(GeometryFactory.toLineStringArray(geomList))
100224             } else if (geom0 instanceof Point$1) {
100225               return this.createMultiPoint(GeometryFactory.toPointArray(geomList))
100226             }
100227             Assert.shouldNeverReachHere('Unhandled class: ' + geom0.getClass().getName());
100228           }
100229           return geom0
100230         };
100231         GeometryFactory.prototype.createMultiPointFromCoords = function createMultiPointFromCoords (coordinates) {
100232           return this.createMultiPoint(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100233         };
100234         GeometryFactory.prototype.createPoint = function createPoint () {
100235           if (arguments.length === 0) {
100236             return this.createPoint(this.getCoordinateSequenceFactory().create([]))
100237           } else if (arguments.length === 1) {
100238             if (arguments[0] instanceof Coordinate) {
100239               var coordinate = arguments[0];
100240               return this.createPoint(coordinate !== null ? this.getCoordinateSequenceFactory().create([coordinate]) : null)
100241             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100242               var coordinates = arguments[0];
100243               return new Point$1(coordinates, this)
100244             }
100245           }
100246         };
100247         GeometryFactory.prototype.getCoordinateSequenceFactory = function getCoordinateSequenceFactory () {
100248           return this._coordinateSequenceFactory
100249         };
100250         GeometryFactory.prototype.createPolygon = function createPolygon () {
100251           if (arguments.length === 0) {
100252             return new Polygon(null, null, this)
100253           } else if (arguments.length === 1) {
100254             if (hasInterface(arguments[0], CoordinateSequence)) {
100255               var coordinates = arguments[0];
100256               return this.createPolygon(this.createLinearRing(coordinates))
100257             } else if (arguments[0] instanceof Array) {
100258               var coordinates$1 = arguments[0];
100259               return this.createPolygon(this.createLinearRing(coordinates$1))
100260             } else if (arguments[0] instanceof LinearRing) {
100261               var shell = arguments[0];
100262               return this.createPolygon(shell, null)
100263             }
100264           } else if (arguments.length === 2) {
100265             var shell$1 = arguments[0];
100266             var holes = arguments[1];
100267             return new Polygon(shell$1, holes, this)
100268           }
100269         };
100270         GeometryFactory.prototype.getSRID = function getSRID () {
100271           return this._SRID
100272         };
100273         GeometryFactory.prototype.createGeometryCollection = function createGeometryCollection () {
100274           if (arguments.length === 0) {
100275             return new GeometryCollection(null, this)
100276           } else if (arguments.length === 1) {
100277             var geometries = arguments[0];
100278             return new GeometryCollection(geometries, this)
100279           }
100280         };
100281         GeometryFactory.prototype.createGeometry = function createGeometry (g) {
100282           var editor = new GeometryEditor(this);
100283           return editor.edit(g, {
100284             edit: function () {
100285               if (arguments.length === 2) {
100286                 var coordSeq = arguments[0];
100287                 // const geometry = arguments[1]
100288                 return this._coordinateSequenceFactory.create(coordSeq)
100289               }
100290             }
100291           })
100292         };
100293         GeometryFactory.prototype.getPrecisionModel = function getPrecisionModel () {
100294           return this._precisionModel
100295         };
100296         GeometryFactory.prototype.createLinearRing = function createLinearRing () {
100297           if (arguments.length === 0) {
100298             return this.createLinearRing(this.getCoordinateSequenceFactory().create([]))
100299           } else if (arguments.length === 1) {
100300             if (arguments[0] instanceof Array) {
100301               var coordinates = arguments[0];
100302               return this.createLinearRing(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100303             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100304               var coordinates$1 = arguments[0];
100305               return new LinearRing(coordinates$1, this)
100306             }
100307           }
100308         };
100309         GeometryFactory.prototype.createMultiPolygon = function createMultiPolygon () {
100310           if (arguments.length === 0) {
100311             return new MultiPolygon(null, this)
100312           } else if (arguments.length === 1) {
100313             var polygons = arguments[0];
100314             return new MultiPolygon(polygons, this)
100315           }
100316         };
100317         GeometryFactory.prototype.createMultiPoint = function createMultiPoint () {
100318             var this$1 = this;
100319
100320           if (arguments.length === 0) {
100321             return new MultiPoint(null, this)
100322           } else if (arguments.length === 1) {
100323             if (arguments[0] instanceof Array) {
100324               var point = arguments[0];
100325               return new MultiPoint(point, this)
100326             } else if (arguments[0] instanceof Array) {
100327               var coordinates = arguments[0];
100328               return this.createMultiPoint(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100329             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100330               var coordinates$1 = arguments[0];
100331               if (coordinates$1 === null) {
100332                 return this.createMultiPoint(new Array(0).fill(null))
100333               }
100334               var points = new Array(coordinates$1.size()).fill(null);
100335               for (var i = 0; i < coordinates$1.size(); i++) {
100336                 var ptSeq = this$1.getCoordinateSequenceFactory().create(1, coordinates$1.getDimension());
100337                 CoordinateSequences.copy(coordinates$1, i, ptSeq, 0, 1);
100338                 points[i] = this$1.createPoint(ptSeq);
100339               }
100340               return this.createMultiPoint(points)
100341             }
100342           }
100343         };
100344         GeometryFactory.prototype.interfaces_ = function interfaces_ () {
100345           return [Serializable]
100346         };
100347         GeometryFactory.prototype.getClass = function getClass () {
100348           return GeometryFactory
100349         };
100350         GeometryFactory.toMultiPolygonArray = function toMultiPolygonArray (multiPolygons) {
100351           var multiPolygonArray = new Array(multiPolygons.size()).fill(null);
100352           return multiPolygons.toArray(multiPolygonArray)
100353         };
100354         GeometryFactory.toGeometryArray = function toGeometryArray (geometries) {
100355           if (geometries === null) { return null }
100356           var geometryArray = new Array(geometries.size()).fill(null);
100357           return geometries.toArray(geometryArray)
100358         };
100359         GeometryFactory.getDefaultCoordinateSequenceFactory = function getDefaultCoordinateSequenceFactory () {
100360           return CoordinateArraySequenceFactory.instance()
100361         };
100362         GeometryFactory.toMultiLineStringArray = function toMultiLineStringArray (multiLineStrings) {
100363           var multiLineStringArray = new Array(multiLineStrings.size()).fill(null);
100364           return multiLineStrings.toArray(multiLineStringArray)
100365         };
100366         GeometryFactory.toLineStringArray = function toLineStringArray (lineStrings) {
100367           var lineStringArray = new Array(lineStrings.size()).fill(null);
100368           return lineStrings.toArray(lineStringArray)
100369         };
100370         GeometryFactory.toMultiPointArray = function toMultiPointArray (multiPoints) {
100371           var multiPointArray = new Array(multiPoints.size()).fill(null);
100372           return multiPoints.toArray(multiPointArray)
100373         };
100374         GeometryFactory.toLinearRingArray = function toLinearRingArray (linearRings) {
100375           var linearRingArray = new Array(linearRings.size()).fill(null);
100376           return linearRings.toArray(linearRingArray)
100377         };
100378         GeometryFactory.toPointArray = function toPointArray (points) {
100379           var pointArray = new Array(points.size()).fill(null);
100380           return points.toArray(pointArray)
100381         };
100382         GeometryFactory.toPolygonArray = function toPolygonArray (polygons) {
100383           var polygonArray = new Array(polygons.size()).fill(null);
100384           return polygons.toArray(polygonArray)
100385         };
100386         GeometryFactory.createPointFromInternalCoord = function createPointFromInternalCoord (coord, exemplar) {
100387           exemplar.getPrecisionModel().makePrecise(coord);
100388           return exemplar.getFactory().createPoint(coord)
100389         };
100390         staticAccessors$2.serialVersionUID.get = function () { return -6820524753094095635 };
100391
100392         Object.defineProperties( GeometryFactory, staticAccessors$2 );
100393
100394         var geometryTypes = ['Point', 'MultiPoint', 'LineString', 'MultiLineString', 'Polygon', 'MultiPolygon'];
100395
100396         /**
100397          * Class for reading and writing Well-Known Text.Create a new parser for GeoJSON
100398          * NOTE: Adapted from OpenLayers 2.11 implementation.
100399          */
100400
100401         /**
100402          * Create a new parser for GeoJSON
100403          *
100404          * @param {GeometryFactory} geometryFactory
100405          * @return An instance of GeoJsonParser.
100406          * @constructor
100407          * @private
100408          */
100409         var GeoJSONParser = function GeoJSONParser (geometryFactory) {
100410           this.geometryFactory = geometryFactory || new GeometryFactory();
100411         };
100412         /**
100413          * Deserialize a GeoJSON object and return the Geometry or Feature(Collection) with JSTS Geometries
100414          *
100415          * @param {}
100416          *        A GeoJSON object.
100417          * @return {} A Geometry instance or object representing a Feature(Collection) with Geometry instances.
100418          * @private
100419          */
100420         GeoJSONParser.prototype.read = function read (json) {
100421           var obj;
100422           if (typeof json === 'string') {
100423             obj = JSON.parse(json);
100424           } else {
100425             obj = json;
100426           }
100427
100428           var type = obj.type;
100429
100430           if (!parse$2[type]) {
100431             throw new Error('Unknown GeoJSON type: ' + obj.type)
100432           }
100433
100434           if (geometryTypes.indexOf(type) !== -1) {
100435             return parse$2[type].apply(this, [obj.coordinates])
100436           } else if (type === 'GeometryCollection') {
100437             return parse$2[type].apply(this, [obj.geometries])
100438           }
100439
100440           // feature or feature collection
100441           return parse$2[type].apply(this, [obj])
100442         };
100443
100444         /**
100445          * Serialize a Geometry object into GeoJSON
100446          *
100447          * @param {Geometry}
100448          *        geometry A Geometry or array of Geometries.
100449          * @return {Object} A GeoJSON object represting the input Geometry/Geometries.
100450          * @private
100451          */
100452         GeoJSONParser.prototype.write = function write (geometry) {
100453           var type = geometry.getGeometryType();
100454
100455           if (!extract[type]) {
100456             throw new Error('Geometry is not supported')
100457           }
100458
100459           return extract[type].apply(this, [geometry])
100460         };
100461
100462         var parse$2 = {
100463           /**
100464            * Parse a GeoJSON Feature object
100465            *
100466            * @param {Object}
100467            *          obj Object to parse.
100468            *
100469            * @return {Object} Feature with geometry/bbox converted to JSTS Geometries.
100470            */
100471           Feature: function (obj) {
100472             var feature = {};
100473
100474             // copy features
100475             for (var key in obj) {
100476               feature[key] = obj[key];
100477             }
100478
100479             // parse geometry
100480             if (obj.geometry) {
100481               var type = obj.geometry.type;
100482               if (!parse$2[type]) {
100483                 throw new Error('Unknown GeoJSON type: ' + obj.type)
100484               }
100485               feature.geometry = this.read(obj.geometry);
100486             }
100487
100488             // bbox
100489             if (obj.bbox) {
100490               feature.bbox = parse$2.bbox.apply(this, [obj.bbox]);
100491             }
100492
100493             return feature
100494           },
100495
100496           /**
100497            * Parse a GeoJSON FeatureCollection object
100498            *
100499            * @param {Object}
100500            *          obj Object to parse.
100501            *
100502            * @return {Object} FeatureCollection with geometry/bbox converted to JSTS Geometries.
100503            */
100504           FeatureCollection: function (obj) {
100505             var this$1 = this;
100506
100507             var featureCollection = {};
100508
100509             if (obj.features) {
100510               featureCollection.features = [];
100511
100512               for (var i = 0; i < obj.features.length; ++i) {
100513                 featureCollection.features.push(this$1.read(obj.features[i]));
100514               }
100515             }
100516
100517             if (obj.bbox) {
100518               featureCollection.bbox = this.parse.bbox.apply(this, [obj.bbox]);
100519             }
100520
100521             return featureCollection
100522           },
100523
100524           /**
100525            * Convert the ordinates in an array to an array of Coordinates
100526            *
100527            * @param {Array}
100528            *          array Array with {Number}s.
100529            *
100530            * @return {Array} Array with Coordinates.
100531            */
100532           coordinates: function (array) {
100533             var coordinates = [];
100534             for (var i = 0; i < array.length; ++i) {
100535               var sub = array[i];
100536               coordinates.push(new Coordinate(sub[0], sub[1]));
100537             }
100538             return coordinates
100539           },
100540
100541           /**
100542            * Convert the bbox to a LinearRing
100543            *
100544            * @param {Array}
100545            *          array Array with [xMin, yMin, xMax, yMax].
100546            *
100547            * @return {Array} Array with Coordinates.
100548            */
100549           bbox: function (array) {
100550             return this.geometryFactory.createLinearRing([
100551               new Coordinate(array[0], array[1]),
100552               new Coordinate(array[2], array[1]),
100553               new Coordinate(array[2], array[3]),
100554               new Coordinate(array[0], array[3]),
100555               new Coordinate(array[0], array[1])
100556             ])
100557           },
100558
100559           /**
100560            * Convert an Array with ordinates to a Point
100561            *
100562            * @param {Array}
100563            *          array Array with ordinates.
100564            *
100565            * @return {Point} Point.
100566            */
100567           Point: function (array) {
100568             var coordinate = new Coordinate(array[0], array[1]);
100569             return this.geometryFactory.createPoint(coordinate)
100570           },
100571
100572           /**
100573            * Convert an Array with coordinates to a MultiPoint
100574            *
100575            * @param {Array}
100576            *          array Array with coordinates.
100577            *
100578            * @return {MultiPoint} MultiPoint.
100579            */
100580           MultiPoint: function (array) {
100581             var this$1 = this;
100582
100583             var points = [];
100584             for (var i = 0; i < array.length; ++i) {
100585               points.push(parse$2.Point.apply(this$1, [array[i]]));
100586             }
100587             return this.geometryFactory.createMultiPoint(points)
100588           },
100589
100590           /**
100591            * Convert an Array with coordinates to a LineString
100592            *
100593            * @param {Array}
100594            *          array Array with coordinates.
100595            *
100596            * @return {LineString} LineString.
100597            */
100598           LineString: function (array) {
100599             var coordinates = parse$2.coordinates.apply(this, [array]);
100600             return this.geometryFactory.createLineString(coordinates)
100601           },
100602
100603           /**
100604            * Convert an Array with coordinates to a MultiLineString
100605            *
100606            * @param {Array}
100607            *          array Array with coordinates.
100608            *
100609            * @return {MultiLineString} MultiLineString.
100610            */
100611           MultiLineString: function (array) {
100612             var this$1 = this;
100613
100614             var lineStrings = [];
100615             for (var i = 0; i < array.length; ++i) {
100616               lineStrings.push(parse$2.LineString.apply(this$1, [array[i]]));
100617             }
100618             return this.geometryFactory.createMultiLineString(lineStrings)
100619           },
100620
100621           /**
100622            * Convert an Array to a Polygon
100623            *
100624            * @param {Array}
100625            *          array Array with shell and holes.
100626            *
100627            * @return {Polygon} Polygon.
100628            */
100629           Polygon: function (array) {
100630             var this$1 = this;
100631
100632             var shellCoordinates = parse$2.coordinates.apply(this, [array[0]]);
100633             var shell = this.geometryFactory.createLinearRing(shellCoordinates);
100634             var holes = [];
100635             for (var i = 1; i < array.length; ++i) {
100636               var hole = array[i];
100637               var coordinates = parse$2.coordinates.apply(this$1, [hole]);
100638               var linearRing = this$1.geometryFactory.createLinearRing(coordinates);
100639               holes.push(linearRing);
100640             }
100641             return this.geometryFactory.createPolygon(shell, holes)
100642           },
100643
100644           /**
100645            * Convert an Array to a MultiPolygon
100646            *
100647            * @param {Array}
100648            *          array Array of arrays with shell and rings.
100649            *
100650            * @return {MultiPolygon} MultiPolygon.
100651            */
100652           MultiPolygon: function (array) {
100653             var this$1 = this;
100654
100655             var polygons = [];
100656             for (var i = 0; i < array.length; ++i) {
100657               var polygon = array[i];
100658               polygons.push(parse$2.Polygon.apply(this$1, [polygon]));
100659             }
100660             return this.geometryFactory.createMultiPolygon(polygons)
100661           },
100662
100663           /**
100664            * Convert an Array to a GeometryCollection
100665            *
100666            * @param {Array}
100667            *          array Array of GeoJSON geometries.
100668            *
100669            * @return {GeometryCollection} GeometryCollection.
100670            */
100671           GeometryCollection: function (array) {
100672             var this$1 = this;
100673
100674             var geometries = [];
100675             for (var i = 0; i < array.length; ++i) {
100676               var geometry = array[i];
100677               geometries.push(this$1.read(geometry));
100678             }
100679             return this.geometryFactory.createGeometryCollection(geometries)
100680           }
100681         };
100682
100683         var extract = {
100684           /**
100685            * Convert a Coordinate to an Array
100686            *
100687            * @param {Coordinate}
100688            *          coordinate Coordinate to convert.
100689            *
100690            * @return {Array} Array of ordinates.
100691            */
100692           coordinate: function (coordinate) {
100693             return [coordinate.x, coordinate.y]
100694           },
100695
100696           /**
100697            * Convert a Point to a GeoJSON object
100698            *
100699            * @param {Point}
100700            *          point Point to convert.
100701            *
100702            * @return {Array} Array of 2 ordinates (paired to a coordinate).
100703            */
100704           Point: function (point) {
100705             var array = extract.coordinate.apply(this, [point.getCoordinate()]);
100706             return {
100707               type: 'Point',
100708               coordinates: array
100709             }
100710           },
100711
100712           /**
100713            * Convert a MultiPoint to a GeoJSON object
100714            *
100715            * @param {MultiPoint}
100716            *          multipoint MultiPoint to convert.
100717            *
100718            * @return {Array} Array of coordinates.
100719            */
100720           MultiPoint: function (multipoint) {
100721             var this$1 = this;
100722
100723             var array = [];
100724             for (var i = 0; i < multipoint._geometries.length; ++i) {
100725               var point = multipoint._geometries[i];
100726               var geoJson = extract.Point.apply(this$1, [point]);
100727               array.push(geoJson.coordinates);
100728             }
100729             return {
100730               type: 'MultiPoint',
100731               coordinates: array
100732             }
100733           },
100734
100735           /**
100736            * Convert a LineString to a GeoJSON object
100737            *
100738            * @param {LineString}
100739            *          linestring LineString to convert.
100740            *
100741            * @return {Array} Array of coordinates.
100742            */
100743           LineString: function (linestring) {
100744             var this$1 = this;
100745
100746             var array = [];
100747             var coordinates = linestring.getCoordinates();
100748             for (var i = 0; i < coordinates.length; ++i) {
100749               var coordinate = coordinates[i];
100750               array.push(extract.coordinate.apply(this$1, [coordinate]));
100751             }
100752             return {
100753               type: 'LineString',
100754               coordinates: array
100755             }
100756           },
100757
100758           /**
100759            * Convert a MultiLineString to a GeoJSON object
100760            *
100761            * @param {MultiLineString}
100762            *          multilinestring MultiLineString to convert.
100763            *
100764            * @return {Array} Array of Array of coordinates.
100765            */
100766           MultiLineString: function (multilinestring) {
100767             var this$1 = this;
100768
100769             var array = [];
100770             for (var i = 0; i < multilinestring._geometries.length; ++i) {
100771               var linestring = multilinestring._geometries[i];
100772               var geoJson = extract.LineString.apply(this$1, [linestring]);
100773               array.push(geoJson.coordinates);
100774             }
100775             return {
100776               type: 'MultiLineString',
100777               coordinates: array
100778             }
100779           },
100780
100781           /**
100782            * Convert a Polygon to a GeoJSON object
100783            *
100784            * @param {Polygon}
100785            *          polygon Polygon to convert.
100786            *
100787            * @return {Array} Array with shell, holes.
100788            */
100789           Polygon: function (polygon) {
100790             var this$1 = this;
100791
100792             var array = [];
100793             var shellGeoJson = extract.LineString.apply(this, [polygon._shell]);
100794             array.push(shellGeoJson.coordinates);
100795             for (var i = 0; i < polygon._holes.length; ++i) {
100796               var hole = polygon._holes[i];
100797               var holeGeoJson = extract.LineString.apply(this$1, [hole]);
100798               array.push(holeGeoJson.coordinates);
100799             }
100800             return {
100801               type: 'Polygon',
100802               coordinates: array
100803             }
100804           },
100805
100806           /**
100807            * Convert a MultiPolygon to a GeoJSON object
100808            *
100809            * @param {MultiPolygon}
100810            *          multipolygon MultiPolygon to convert.
100811            *
100812            * @return {Array} Array of polygons.
100813            */
100814           MultiPolygon: function (multipolygon) {
100815             var this$1 = this;
100816
100817             var array = [];
100818             for (var i = 0; i < multipolygon._geometries.length; ++i) {
100819               var polygon = multipolygon._geometries[i];
100820               var geoJson = extract.Polygon.apply(this$1, [polygon]);
100821               array.push(geoJson.coordinates);
100822             }
100823             return {
100824               type: 'MultiPolygon',
100825               coordinates: array
100826             }
100827           },
100828
100829           /**
100830            * Convert a GeometryCollection to a GeoJSON object
100831            *
100832            * @param {GeometryCollection}
100833            *          collection GeometryCollection to convert.
100834            *
100835            * @return {Array} Array of geometries.
100836            */
100837           GeometryCollection: function (collection) {
100838             var this$1 = this;
100839
100840             var array = [];
100841             for (var i = 0; i < collection._geometries.length; ++i) {
100842               var geometry = collection._geometries[i];
100843               var type = geometry.getGeometryType();
100844               array.push(extract[type].apply(this$1, [geometry]));
100845             }
100846             return {
100847               type: 'GeometryCollection',
100848               geometries: array
100849             }
100850           }
100851         };
100852
100853         /**
100854          * Converts a geometry in GeoJSON to a {@link Geometry}.
100855          */
100856
100857         /**
100858          * A <code>GeoJSONReader</code> is parameterized by a <code>GeometryFactory</code>,
100859          * to allow it to create <code>Geometry</code> objects of the appropriate
100860          * implementation. In particular, the <code>GeometryFactory</code> determines
100861          * the <code>PrecisionModel</code> and <code>SRID</code> that is used.
100862          *
100863          * @param {GeometryFactory} geometryFactory
100864          * @constructor
100865          */
100866         var GeoJSONReader = function GeoJSONReader (geometryFactory) {
100867           this.geometryFactory = geometryFactory || new GeometryFactory();
100868           this.precisionModel = this.geometryFactory.getPrecisionModel();
100869           this.parser = new GeoJSONParser(this.geometryFactory);
100870         };
100871         /**
100872          * Reads a GeoJSON representation of a {@link Geometry}
100873          *
100874          * Will also parse GeoJSON Features/FeatureCollections as custom objects.
100875          *
100876          * @param {Object|String} geoJson a GeoJSON Object or String.
100877          * @return {Geometry|Object} a <code>Geometry or Feature/FeatureCollection representation.</code>
100878          * @memberof GeoJSONReader
100879          */
100880         GeoJSONReader.prototype.read = function read (geoJson) {
100881           var geometry = this.parser.read(geoJson);
100882
100883           if (this.precisionModel.getType() === PrecisionModel.FIXED) {
100884             this.reducePrecision(geometry);
100885           }
100886
100887           return geometry
100888         };
100889
100890         // NOTE: this is a hack
100891         GeoJSONReader.prototype.reducePrecision = function reducePrecision (geometry) {
100892             var this$1 = this;
100893
100894           var i, len;
100895
100896           if (geometry.coordinate) {
100897             this.precisionModel.makePrecise(geometry.coordinate);
100898           } else if (geometry.points) {
100899             for (i = 0, len = geometry.points.length; i < len; i++) {
100900               this$1.precisionModel.makePrecise(geometry.points[i]);
100901             }
100902           } else if (geometry.geometries) {
100903             for (i = 0, len = geometry.geometries.length; i < len; i++) {
100904               this$1.reducePrecision(geometry.geometries[i]);
100905             }
100906           }
100907         };
100908
100909         /**
100910          * @module GeoJSONWriter
100911          */
100912
100913         /**
100914          * Writes the GeoJSON representation of a {@link Geometry}. The
100915          * The GeoJSON format is defined <A
100916          * HREF="http://geojson.org/geojson-spec.html">here</A>.
100917          */
100918
100919         /**
100920          * The <code>GeoJSONWriter</code> outputs coordinates rounded to the precision
100921          * model. Only the maximum number of decimal places necessary to represent the
100922          * ordinates to the required precision will be output.
100923          *
100924          * @param {GeometryFactory} geometryFactory
100925          * @constructor
100926          */
100927         var GeoJSONWriter = function GeoJSONWriter () {
100928           this.parser = new GeoJSONParser(this.geometryFactory);
100929         };
100930         /**
100931          * Converts a <code>Geometry</code> to its GeoJSON representation.
100932          *
100933          * @param {Geometry}
100934          *        geometry a <code>Geometry</code> to process.
100935          * @return {Object} The GeoJSON representation of the Geometry.
100936          * @memberof GeoJSONWriter
100937          */
100938         GeoJSONWriter.prototype.write = function write (geometry) {
100939           return this.parser.write(geometry)
100940         };
100941
100942         /* eslint-disable no-undef */
100943
100944         // io
100945
100946         var Position = function Position () {};
100947
100948         var staticAccessors$20 = { ON: { configurable: true },LEFT: { configurable: true },RIGHT: { configurable: true } };
100949
100950         Position.prototype.interfaces_ = function interfaces_ () {
100951           return []
100952         };
100953         Position.prototype.getClass = function getClass () {
100954           return Position
100955         };
100956         Position.opposite = function opposite (position) {
100957           if (position === Position.LEFT) { return Position.RIGHT }
100958           if (position === Position.RIGHT) { return Position.LEFT }
100959           return position
100960         };
100961         staticAccessors$20.ON.get = function () { return 0 };
100962         staticAccessors$20.LEFT.get = function () { return 1 };
100963         staticAccessors$20.RIGHT.get = function () { return 2 };
100964
100965         Object.defineProperties( Position, staticAccessors$20 );
100966
100967         /**
100968          * @param {string=} message Optional message
100969          * @extends {Error}
100970          * @constructor
100971          * @private
100972          */
100973         function EmptyStackException (message) {
100974           this.message = message || '';
100975         }
100976         EmptyStackException.prototype = new Error();
100977
100978         /**
100979          * @type {string}
100980          */
100981         EmptyStackException.prototype.name = 'EmptyStackException';
100982
100983         /**
100984          * @see http://download.oracle.com/javase/6/docs/api/java/util/Stack.html
100985          *
100986          * @extends {List}
100987          * @constructor
100988          * @private
100989          */
100990         function Stack () {
100991           /**
100992            * @type {Array}
100993            * @private
100994            */
100995           this.array_ = [];
100996         }
100997         Stack.prototype = new List();
100998
100999         /**
101000          * @override
101001          */
101002         Stack.prototype.add = function (e) {
101003           this.array_.push(e);
101004           return true
101005         };
101006
101007         /**
101008          * @override
101009          */
101010         Stack.prototype.get = function (index) {
101011           if (index < 0 || index >= this.size()) {
101012             throw new Error()
101013           }
101014
101015           return this.array_[index]
101016         };
101017
101018         /**
101019          * Pushes an item onto the top of this stack.
101020          * @param {Object} e
101021          * @return {Object}
101022          */
101023         Stack.prototype.push = function (e) {
101024           this.array_.push(e);
101025           return e
101026         };
101027
101028         /**
101029          * Pushes an item onto the top of this stack.
101030          * @param {Object} e
101031          * @return {Object}
101032          */
101033         Stack.prototype.pop = function (e) {
101034           if (this.array_.length === 0) {
101035             throw new EmptyStackException()
101036           }
101037
101038           return this.array_.pop()
101039         };
101040
101041         /**
101042          * Looks at the object at the top of this stack without removing it from the
101043          * stack.
101044          * @return {Object}
101045          */
101046         Stack.prototype.peek = function () {
101047           if (this.array_.length === 0) {
101048             throw new EmptyStackException()
101049           }
101050
101051           return this.array_[this.array_.length - 1]
101052         };
101053
101054         /**
101055          * Tests if this stack is empty.
101056          * @return {boolean} true if and only if this stack contains no items; false
101057          *         otherwise.
101058          */
101059         Stack.prototype.empty = function () {
101060           if (this.array_.length === 0) {
101061             return true
101062           } else {
101063             return false
101064           }
101065         };
101066
101067         /**
101068          * @return {boolean}
101069          */
101070         Stack.prototype.isEmpty = function () {
101071           return this.empty()
101072         };
101073
101074         /**
101075          * Returns the 1-based position where an object is on this stack. If the object
101076          * o occurs as an item in this stack, this method returns the distance from the
101077          * top of the stack of the occurrence nearest the top of the stack; the topmost
101078          * item on the stack is considered to be at distance 1. The equals method is
101079          * used to compare o to the items in this stack.
101080          *
101081          * NOTE: does not currently actually use equals. (=== is used)
101082          *
101083          * @param {Object} o
101084          * @return {number} the 1-based position from the top of the stack where the
101085          *         object is located; the return value -1 indicates that the object is
101086          *         not on the stack.
101087          */
101088         Stack.prototype.search = function (o) {
101089           return this.array_.indexOf(o)
101090         };
101091
101092         /**
101093          * @return {number}
101094          * @export
101095          */
101096         Stack.prototype.size = function () {
101097           return this.array_.length
101098         };
101099
101100         /**
101101          * @return {Array}
101102          */
101103         Stack.prototype.toArray = function () {
101104           var this$1 = this;
101105
101106           var array = [];
101107
101108           for (var i = 0, len = this.array_.length; i < len; i++) {
101109             array.push(this$1.array_[i]);
101110           }
101111
101112           return array
101113         };
101114
101115         var RightmostEdgeFinder = function RightmostEdgeFinder () {
101116           this._minIndex = -1;
101117           this._minCoord = null;
101118           this._minDe = null;
101119           this._orientedDe = null;
101120         };
101121         RightmostEdgeFinder.prototype.getCoordinate = function getCoordinate () {
101122           return this._minCoord
101123         };
101124         RightmostEdgeFinder.prototype.getRightmostSide = function getRightmostSide (de, index) {
101125           var side = this.getRightmostSideOfSegment(de, index);
101126           if (side < 0) { side = this.getRightmostSideOfSegment(de, index - 1); }
101127           if (side < 0) {
101128             this._minCoord = null;
101129             this.checkForRightmostCoordinate(de);
101130           }
101131           return side
101132         };
101133         RightmostEdgeFinder.prototype.findRightmostEdgeAtVertex = function findRightmostEdgeAtVertex () {
101134           var pts = this._minDe.getEdge().getCoordinates();
101135           Assert.isTrue(this._minIndex > 0 && this._minIndex < pts.length, 'rightmost point expected to be interior vertex of edge');
101136           var pPrev = pts[this._minIndex - 1];
101137           var pNext = pts[this._minIndex + 1];
101138           var orientation = CGAlgorithms.computeOrientation(this._minCoord, pNext, pPrev);
101139           var usePrev = false;
101140           if (pPrev.y < this._minCoord.y && pNext.y < this._minCoord.y && orientation === CGAlgorithms.COUNTERCLOCKWISE) {
101141             usePrev = true;
101142           } else if (pPrev.y > this._minCoord.y && pNext.y > this._minCoord.y && orientation === CGAlgorithms.CLOCKWISE) {
101143             usePrev = true;
101144           }
101145           if (usePrev) {
101146             this._minIndex = this._minIndex - 1;
101147           }
101148         };
101149         RightmostEdgeFinder.prototype.getRightmostSideOfSegment = function getRightmostSideOfSegment (de, i) {
101150           var e = de.getEdge();
101151           var coord = e.getCoordinates();
101152           if (i < 0 || i + 1 >= coord.length) { return -1 }
101153           if (coord[i].y === coord[i + 1].y) { return -1 }
101154           var pos = Position.LEFT;
101155           if (coord[i].y < coord[i + 1].y) { pos = Position.RIGHT; }
101156           return pos
101157         };
101158         RightmostEdgeFinder.prototype.getEdge = function getEdge () {
101159           return this._orientedDe
101160         };
101161         RightmostEdgeFinder.prototype.checkForRightmostCoordinate = function checkForRightmostCoordinate (de) {
101162             var this$1 = this;
101163
101164           var coord = de.getEdge().getCoordinates();
101165           for (var i = 0; i < coord.length - 1; i++) {
101166             if (this$1._minCoord === null || coord[i].x > this$1._minCoord.x) {
101167               this$1._minDe = de;
101168               this$1._minIndex = i;
101169               this$1._minCoord = coord[i];
101170             }
101171           }
101172         };
101173         RightmostEdgeFinder.prototype.findRightmostEdgeAtNode = function findRightmostEdgeAtNode () {
101174           var node = this._minDe.getNode();
101175           var star = node.getEdges();
101176           this._minDe = star.getRightmostEdge();
101177           if (!this._minDe.isForward()) {
101178             this._minDe = this._minDe.getSym();
101179             this._minIndex = this._minDe.getEdge().getCoordinates().length - 1;
101180           }
101181         };
101182         RightmostEdgeFinder.prototype.findEdge = function findEdge (dirEdgeList) {
101183             var this$1 = this;
101184
101185           for (var i = dirEdgeList.iterator(); i.hasNext();) {
101186             var de = i.next();
101187             if (!de.isForward()) { continue }
101188             this$1.checkForRightmostCoordinate(de);
101189           }
101190           Assert.isTrue(this._minIndex !== 0 || this._minCoord.equals(this._minDe.getCoordinate()), 'inconsistency in rightmost processing');
101191           if (this._minIndex === 0) {
101192             this.findRightmostEdgeAtNode();
101193           } else {
101194             this.findRightmostEdgeAtVertex();
101195           }
101196           this._orientedDe = this._minDe;
101197           var rightmostSide = this.getRightmostSide(this._minDe, this._minIndex);
101198           if (rightmostSide === Position.LEFT) {
101199             this._orientedDe = this._minDe.getSym();
101200           }
101201         };
101202         RightmostEdgeFinder.prototype.interfaces_ = function interfaces_ () {
101203           return []
101204         };
101205         RightmostEdgeFinder.prototype.getClass = function getClass () {
101206           return RightmostEdgeFinder
101207         };
101208
101209         var TopologyException = (function (RuntimeException$$1) {
101210           function TopologyException (msg, pt) {
101211             RuntimeException$$1.call(this, TopologyException.msgWithCoord(msg, pt));
101212             this.pt = pt ? new Coordinate(pt) : null;
101213             this.name = 'TopologyException';
101214           }
101215
101216           if ( RuntimeException$$1 ) TopologyException.__proto__ = RuntimeException$$1;
101217           TopologyException.prototype = Object.create( RuntimeException$$1 && RuntimeException$$1.prototype );
101218           TopologyException.prototype.constructor = TopologyException;
101219           TopologyException.prototype.getCoordinate = function getCoordinate () {
101220             return this.pt
101221           };
101222           TopologyException.prototype.interfaces_ = function interfaces_ () {
101223             return []
101224           };
101225           TopologyException.prototype.getClass = function getClass () {
101226             return TopologyException
101227           };
101228           TopologyException.msgWithCoord = function msgWithCoord (msg, pt) {
101229             if (!pt) { return msg + ' [ ' + pt + ' ]' }
101230             return msg
101231           };
101232
101233           return TopologyException;
101234         }(RuntimeException));
101235
101236         var LinkedList = function LinkedList () {
101237           this.array_ = [];
101238         };
101239         LinkedList.prototype.addLast = function addLast (e) {
101240           this.array_.push(e);
101241         };
101242         LinkedList.prototype.removeFirst = function removeFirst () {
101243           return this.array_.shift()
101244         };
101245         LinkedList.prototype.isEmpty = function isEmpty () {
101246           return this.array_.length === 0
101247         };
101248
101249         var BufferSubgraph = function BufferSubgraph () {
101250           this._finder = null;
101251           this._dirEdgeList = new ArrayList();
101252           this._nodes = new ArrayList();
101253           this._rightMostCoord = null;
101254           this._env = null;
101255           this._finder = new RightmostEdgeFinder();
101256         };
101257         BufferSubgraph.prototype.clearVisitedEdges = function clearVisitedEdges () {
101258           for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101259             var de = it.next();
101260             de.setVisited(false);
101261           }
101262         };
101263         BufferSubgraph.prototype.getRightmostCoordinate = function getRightmostCoordinate () {
101264           return this._rightMostCoord
101265         };
101266         BufferSubgraph.prototype.computeNodeDepth = function computeNodeDepth (n) {
101267             var this$1 = this;
101268
101269           var startEdge = null;
101270           for (var i = n.getEdges().iterator(); i.hasNext();) {
101271             var de = i.next();
101272             if (de.isVisited() || de.getSym().isVisited()) {
101273               startEdge = de;
101274               break
101275             }
101276           }
101277           if (startEdge === null) { throw new TopologyException('unable to find edge to compute depths at ' + n.getCoordinate()) }
101278           n.getEdges().computeDepths(startEdge);
101279           for (var i$1 = n.getEdges().iterator(); i$1.hasNext();) {
101280             var de$1 = i$1.next();
101281             de$1.setVisited(true);
101282             this$1.copySymDepths(de$1);
101283           }
101284         };
101285         BufferSubgraph.prototype.computeDepth = function computeDepth (outsideDepth) {
101286           this.clearVisitedEdges();
101287           var de = this._finder.getEdge();
101288           // const n = de.getNode()
101289           // const label = de.getLabel()
101290           de.setEdgeDepths(Position.RIGHT, outsideDepth);
101291           this.copySymDepths(de);
101292           this.computeDepths(de);
101293         };
101294         BufferSubgraph.prototype.create = function create (node) {
101295           this.addReachable(node);
101296           this._finder.findEdge(this._dirEdgeList);
101297           this._rightMostCoord = this._finder.getCoordinate();
101298         };
101299         BufferSubgraph.prototype.findResultEdges = function findResultEdges () {
101300           for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101301             var de = it.next();
101302             if (de.getDepth(Position.RIGHT) >= 1 && de.getDepth(Position.LEFT) <= 0 && !de.isInteriorAreaEdge()) {
101303               de.setInResult(true);
101304             }
101305           }
101306         };
101307         BufferSubgraph.prototype.computeDepths = function computeDepths (startEdge) {
101308             var this$1 = this;
101309
101310           var nodesVisited = new HashSet();
101311           var nodeQueue = new LinkedList();
101312           var startNode = startEdge.getNode();
101313           nodeQueue.addLast(startNode);
101314           nodesVisited.add(startNode);
101315           startEdge.setVisited(true);
101316           while (!nodeQueue.isEmpty()) {
101317             var n = nodeQueue.removeFirst();
101318             nodesVisited.add(n);
101319             this$1.computeNodeDepth(n);
101320             for (var i = n.getEdges().iterator(); i.hasNext();) {
101321               var de = i.next();
101322               var sym = de.getSym();
101323               if (sym.isVisited()) { continue }
101324               var adjNode = sym.getNode();
101325               if (!nodesVisited.contains(adjNode)) {
101326                 nodeQueue.addLast(adjNode);
101327                 nodesVisited.add(adjNode);
101328               }
101329             }
101330           }
101331         };
101332         BufferSubgraph.prototype.compareTo = function compareTo (o) {
101333           var graph = o;
101334           if (this._rightMostCoord.x < graph._rightMostCoord.x) {
101335             return -1
101336           }
101337           if (this._rightMostCoord.x > graph._rightMostCoord.x) {
101338             return 1
101339           }
101340           return 0
101341         };
101342         BufferSubgraph.prototype.getEnvelope = function getEnvelope () {
101343           if (this._env === null) {
101344             var edgeEnv = new Envelope();
101345             for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101346               var dirEdge = it.next();
101347               var pts = dirEdge.getEdge().getCoordinates();
101348               for (var i = 0; i < pts.length - 1; i++) {
101349                 edgeEnv.expandToInclude(pts[i]);
101350               }
101351             }
101352             this._env = edgeEnv;
101353           }
101354           return this._env
101355         };
101356         BufferSubgraph.prototype.addReachable = function addReachable (startNode) {
101357             var this$1 = this;
101358
101359           var nodeStack = new Stack();
101360           nodeStack.add(startNode);
101361           while (!nodeStack.empty()) {
101362             var node = nodeStack.pop();
101363             this$1.add(node, nodeStack);
101364           }
101365         };
101366         BufferSubgraph.prototype.copySymDepths = function copySymDepths (de) {
101367           var sym = de.getSym();
101368           sym.setDepth(Position.LEFT, de.getDepth(Position.RIGHT));
101369           sym.setDepth(Position.RIGHT, de.getDepth(Position.LEFT));
101370         };
101371         BufferSubgraph.prototype.add = function add (node, nodeStack) {
101372             var this$1 = this;
101373
101374           node.setVisited(true);
101375           this._nodes.add(node);
101376           for (var i = node.getEdges().iterator(); i.hasNext();) {
101377             var de = i.next();
101378             this$1._dirEdgeList.add(de);
101379             var sym = de.getSym();
101380             var symNode = sym.getNode();
101381             if (!symNode.isVisited()) { nodeStack.push(symNode); }
101382           }
101383         };
101384         BufferSubgraph.prototype.getNodes = function getNodes () {
101385           return this._nodes
101386         };
101387         BufferSubgraph.prototype.getDirectedEdges = function getDirectedEdges () {
101388           return this._dirEdgeList
101389         };
101390         BufferSubgraph.prototype.interfaces_ = function interfaces_ () {
101391           return [Comparable]
101392         };
101393         BufferSubgraph.prototype.getClass = function getClass () {
101394           return BufferSubgraph
101395         };
101396
101397         var TopologyLocation = function TopologyLocation () {
101398           var this$1 = this;
101399
101400           this.location = null;
101401           if (arguments.length === 1) {
101402             if (arguments[0] instanceof Array) {
101403               var location = arguments[0];
101404               this.init(location.length);
101405             } else if (Number.isInteger(arguments[0])) {
101406               var on = arguments[0];
101407               this.init(1);
101408               this.location[Position.ON] = on;
101409             } else if (arguments[0] instanceof TopologyLocation) {
101410               var gl = arguments[0];
101411               this.init(gl.location.length);
101412               if (gl !== null) {
101413                 for (var i = 0; i < this.location.length; i++) {
101414                   this$1.location[i] = gl.location[i];
101415                 }
101416               }
101417             }
101418           } else if (arguments.length === 3) {
101419             var on$1 = arguments[0];
101420             var left = arguments[1];
101421             var right = arguments[2];
101422             this.init(3);
101423             this.location[Position.ON] = on$1;
101424             this.location[Position.LEFT] = left;
101425             this.location[Position.RIGHT] = right;
101426           }
101427         };
101428         TopologyLocation.prototype.setAllLocations = function setAllLocations (locValue) {
101429             var this$1 = this;
101430
101431           for (var i = 0; i < this.location.length; i++) {
101432             this$1.location[i] = locValue;
101433           }
101434         };
101435         TopologyLocation.prototype.isNull = function isNull () {
101436             var this$1 = this;
101437
101438           for (var i = 0; i < this.location.length; i++) {
101439             if (this$1.location[i] !== Location.NONE) { return false }
101440           }
101441           return true
101442         };
101443         TopologyLocation.prototype.setAllLocationsIfNull = function setAllLocationsIfNull (locValue) {
101444             var this$1 = this;
101445
101446           for (var i = 0; i < this.location.length; i++) {
101447             if (this$1.location[i] === Location.NONE) { this$1.location[i] = locValue; }
101448           }
101449         };
101450         TopologyLocation.prototype.isLine = function isLine () {
101451           return this.location.length === 1
101452         };
101453         TopologyLocation.prototype.merge = function merge (gl) {
101454             var this$1 = this;
101455
101456           if (gl.location.length > this.location.length) {
101457             var newLoc = new Array(3).fill(null);
101458             newLoc[Position.ON] = this.location[Position.ON];
101459             newLoc[Position.LEFT] = Location.NONE;
101460             newLoc[Position.RIGHT] = Location.NONE;
101461             this.location = newLoc;
101462           }
101463           for (var i = 0; i < this.location.length; i++) {
101464             if (this$1.location[i] === Location.NONE && i < gl.location.length) { this$1.location[i] = gl.location[i]; }
101465           }
101466         };
101467         TopologyLocation.prototype.getLocations = function getLocations () {
101468           return this.location
101469         };
101470         TopologyLocation.prototype.flip = function flip () {
101471           if (this.location.length <= 1) { return null }
101472           var temp = this.location[Position.LEFT];
101473           this.location[Position.LEFT] = this.location[Position.RIGHT];
101474           this.location[Position.RIGHT] = temp;
101475         };
101476         TopologyLocation.prototype.toString = function toString () {
101477           var buf = new StringBuffer();
101478           if (this.location.length > 1) { buf.append(Location.toLocationSymbol(this.location[Position.LEFT])); }
101479           buf.append(Location.toLocationSymbol(this.location[Position.ON]));
101480           if (this.location.length > 1) { buf.append(Location.toLocationSymbol(this.location[Position.RIGHT])); }
101481           return buf.toString()
101482         };
101483         TopologyLocation.prototype.setLocations = function setLocations (on, left, right) {
101484           this.location[Position.ON] = on;
101485           this.location[Position.LEFT] = left;
101486           this.location[Position.RIGHT] = right;
101487         };
101488         TopologyLocation.prototype.get = function get (posIndex) {
101489           if (posIndex < this.location.length) { return this.location[posIndex] }
101490           return Location.NONE
101491         };
101492         TopologyLocation.prototype.isArea = function isArea () {
101493           return this.location.length > 1
101494         };
101495         TopologyLocation.prototype.isAnyNull = function isAnyNull () {
101496             var this$1 = this;
101497
101498           for (var i = 0; i < this.location.length; i++) {
101499             if (this$1.location[i] === Location.NONE) { return true }
101500           }
101501           return false
101502         };
101503         TopologyLocation.prototype.setLocation = function setLocation () {
101504           if (arguments.length === 1) {
101505             var locValue = arguments[0];
101506             this.setLocation(Position.ON, locValue);
101507           } else if (arguments.length === 2) {
101508             var locIndex = arguments[0];
101509             var locValue$1 = arguments[1];
101510             this.location[locIndex] = locValue$1;
101511           }
101512         };
101513         TopologyLocation.prototype.init = function init (size) {
101514           this.location = new Array(size).fill(null);
101515           this.setAllLocations(Location.NONE);
101516         };
101517         TopologyLocation.prototype.isEqualOnSide = function isEqualOnSide (le, locIndex) {
101518           return this.location[locIndex] === le.location[locIndex]
101519         };
101520         TopologyLocation.prototype.allPositionsEqual = function allPositionsEqual (loc) {
101521             var this$1 = this;
101522
101523           for (var i = 0; i < this.location.length; i++) {
101524             if (this$1.location[i] !== loc) { return false }
101525           }
101526           return true
101527         };
101528         TopologyLocation.prototype.interfaces_ = function interfaces_ () {
101529           return []
101530         };
101531         TopologyLocation.prototype.getClass = function getClass () {
101532           return TopologyLocation
101533         };
101534
101535         var Label = function Label () {
101536           this.elt = new Array(2).fill(null);
101537           if (arguments.length === 1) {
101538             if (Number.isInteger(arguments[0])) {
101539               var onLoc = arguments[0];
101540               this.elt[0] = new TopologyLocation(onLoc);
101541               this.elt[1] = new TopologyLocation(onLoc);
101542             } else if (arguments[0] instanceof Label) {
101543               var lbl = arguments[0];
101544               this.elt[0] = new TopologyLocation(lbl.elt[0]);
101545               this.elt[1] = new TopologyLocation(lbl.elt[1]);
101546             }
101547           } else if (arguments.length === 2) {
101548             var geomIndex = arguments[0];
101549             var onLoc$1 = arguments[1];
101550             this.elt[0] = new TopologyLocation(Location.NONE);
101551             this.elt[1] = new TopologyLocation(Location.NONE);
101552             this.elt[geomIndex].setLocation(onLoc$1);
101553           } else if (arguments.length === 3) {
101554             var onLoc$2 = arguments[0];
101555             var leftLoc = arguments[1];
101556             var rightLoc = arguments[2];
101557             this.elt[0] = new TopologyLocation(onLoc$2, leftLoc, rightLoc);
101558             this.elt[1] = new TopologyLocation(onLoc$2, leftLoc, rightLoc);
101559           } else if (arguments.length === 4) {
101560             var geomIndex$1 = arguments[0];
101561             var onLoc$3 = arguments[1];
101562             var leftLoc$1 = arguments[2];
101563             var rightLoc$1 = arguments[3];
101564             this.elt[0] = new TopologyLocation(Location.NONE, Location.NONE, Location.NONE);
101565             this.elt[1] = new TopologyLocation(Location.NONE, Location.NONE, Location.NONE);
101566             this.elt[geomIndex$1].setLocations(onLoc$3, leftLoc$1, rightLoc$1);
101567           }
101568         };
101569         Label.prototype.getGeometryCount = function getGeometryCount () {
101570           var count = 0;
101571           if (!this.elt[0].isNull()) { count++; }
101572           if (!this.elt[1].isNull()) { count++; }
101573           return count
101574         };
101575         Label.prototype.setAllLocations = function setAllLocations (geomIndex, location) {
101576           this.elt[geomIndex].setAllLocations(location);
101577         };
101578         Label.prototype.isNull = function isNull (geomIndex) {
101579           return this.elt[geomIndex].isNull()
101580         };
101581         Label.prototype.setAllLocationsIfNull = function setAllLocationsIfNull () {
101582           if (arguments.length === 1) {
101583             var location = arguments[0];
101584             this.setAllLocationsIfNull(0, location);
101585             this.setAllLocationsIfNull(1, location);
101586           } else if (arguments.length === 2) {
101587             var geomIndex = arguments[0];
101588             var location$1 = arguments[1];
101589             this.elt[geomIndex].setAllLocationsIfNull(location$1);
101590           }
101591         };
101592         Label.prototype.isLine = function isLine (geomIndex) {
101593           return this.elt[geomIndex].isLine()
101594         };
101595         Label.prototype.merge = function merge (lbl) {
101596             var this$1 = this;
101597
101598           for (var i = 0; i < 2; i++) {
101599             if (this$1.elt[i] === null && lbl.elt[i] !== null) {
101600               this$1.elt[i] = new TopologyLocation(lbl.elt[i]);
101601             } else {
101602               this$1.elt[i].merge(lbl.elt[i]);
101603             }
101604           }
101605         };
101606         Label.prototype.flip = function flip () {
101607           this.elt[0].flip();
101608           this.elt[1].flip();
101609         };
101610         Label.prototype.getLocation = function getLocation () {
101611           if (arguments.length === 1) {
101612             var geomIndex = arguments[0];
101613             return this.elt[geomIndex].get(Position.ON)
101614           } else if (arguments.length === 2) {
101615             var geomIndex$1 = arguments[0];
101616             var posIndex = arguments[1];
101617             return this.elt[geomIndex$1].get(posIndex)
101618           }
101619         };
101620         Label.prototype.toString = function toString () {
101621           var buf = new StringBuffer();
101622           if (this.elt[0] !== null) {
101623             buf.append('A:');
101624             buf.append(this.elt[0].toString());
101625           }
101626           if (this.elt[1] !== null) {
101627             buf.append(' B:');
101628             buf.append(this.elt[1].toString());
101629           }
101630           return buf.toString()
101631         };
101632         Label.prototype.isArea = function isArea () {
101633           if (arguments.length === 0) {
101634             return this.elt[0].isArea() || this.elt[1].isArea()
101635           } else if (arguments.length === 1) {
101636             var geomIndex = arguments[0];
101637             return this.elt[geomIndex].isArea()
101638           }
101639         };
101640         Label.prototype.isAnyNull = function isAnyNull (geomIndex) {
101641           return this.elt[geomIndex].isAnyNull()
101642         };
101643         Label.prototype.setLocation = function setLocation () {
101644           if (arguments.length === 2) {
101645             var geomIndex = arguments[0];
101646             var location = arguments[1];
101647             this.elt[geomIndex].setLocation(Position.ON, location);
101648           } else if (arguments.length === 3) {
101649             var geomIndex$1 = arguments[0];
101650             var posIndex = arguments[1];
101651             var location$1 = arguments[2];
101652             this.elt[geomIndex$1].setLocation(posIndex, location$1);
101653           }
101654         };
101655         Label.prototype.isEqualOnSide = function isEqualOnSide (lbl, side) {
101656           return this.elt[0].isEqualOnSide(lbl.elt[0], side) && this.elt[1].isEqualOnSide(lbl.elt[1], side)
101657         };
101658         Label.prototype.allPositionsEqual = function allPositionsEqual (geomIndex, loc) {
101659           return this.elt[geomIndex].allPositionsEqual(loc)
101660         };
101661         Label.prototype.toLine = function toLine (geomIndex) {
101662           if (this.elt[geomIndex].isArea()) { this.elt[geomIndex] = new TopologyLocation(this.elt[geomIndex].location[0]); }
101663         };
101664         Label.prototype.interfaces_ = function interfaces_ () {
101665           return []
101666         };
101667         Label.prototype.getClass = function getClass () {
101668           return Label
101669         };
101670         Label.toLineLabel = function toLineLabel (label) {
101671           var lineLabel = new Label(Location.NONE);
101672           for (var i = 0; i < 2; i++) {
101673             lineLabel.setLocation(i, label.getLocation(i));
101674           }
101675           return lineLabel
101676         };
101677
101678         var EdgeRing = function EdgeRing () {
101679           this._startDe = null;
101680           this._maxNodeDegree = -1;
101681           this._edges = new ArrayList();
101682           this._pts = new ArrayList();
101683           this._label = new Label(Location.NONE);
101684           this._ring = null;
101685           this._isHole = null;
101686           this._shell = null;
101687           this._holes = new ArrayList();
101688           this._geometryFactory = null;
101689           var start = arguments[0];
101690           var geometryFactory = arguments[1];
101691           this._geometryFactory = geometryFactory;
101692           this.computePoints(start);
101693           this.computeRing();
101694         };
101695         EdgeRing.prototype.computeRing = function computeRing () {
101696             var this$1 = this;
101697
101698           if (this._ring !== null) { return null }
101699           var coord = new Array(this._pts.size()).fill(null);
101700           for (var i = 0; i < this._pts.size(); i++) {
101701             coord[i] = this$1._pts.get(i);
101702           }
101703           this._ring = this._geometryFactory.createLinearRing(coord);
101704           this._isHole = CGAlgorithms.isCCW(this._ring.getCoordinates());
101705         };
101706         EdgeRing.prototype.isIsolated = function isIsolated () {
101707           return this._label.getGeometryCount() === 1
101708         };
101709         EdgeRing.prototype.computePoints = function computePoints (start) {
101710             var this$1 = this;
101711
101712           this._startDe = start;
101713           var de = start;
101714           var isFirstEdge = true;
101715           do {
101716             if (de === null) { throw new TopologyException('Found null DirectedEdge') }
101717             if (de.getEdgeRing() === this$1) { throw new TopologyException('Directed Edge visited twice during ring-building at ' + de.getCoordinate()) }
101718             this$1._edges.add(de);
101719             var label = de.getLabel();
101720             Assert.isTrue(label.isArea());
101721             this$1.mergeLabel(label);
101722             this$1.addPoints(de.getEdge(), de.isForward(), isFirstEdge);
101723             isFirstEdge = false;
101724             this$1.setEdgeRing(de, this$1);
101725             de = this$1.getNext(de);
101726           } while (de !== this._startDe)
101727         };
101728         EdgeRing.prototype.getLinearRing = function getLinearRing () {
101729           return this._ring
101730         };
101731         EdgeRing.prototype.getCoordinate = function getCoordinate (i) {
101732           return this._pts.get(i)
101733         };
101734         EdgeRing.prototype.computeMaxNodeDegree = function computeMaxNodeDegree () {
101735             var this$1 = this;
101736
101737           this._maxNodeDegree = 0;
101738           var de = this._startDe;
101739           do {
101740             var node = de.getNode();
101741             var degree = node.getEdges().getOutgoingDegree(this$1);
101742             if (degree > this$1._maxNodeDegree) { this$1._maxNodeDegree = degree; }
101743             de = this$1.getNext(de);
101744           } while (de !== this._startDe)
101745           this._maxNodeDegree *= 2;
101746         };
101747         EdgeRing.prototype.addPoints = function addPoints (edge, isForward, isFirstEdge) {
101748             var this$1 = this;
101749
101750           var edgePts = edge.getCoordinates();
101751           if (isForward) {
101752             var startIndex = 1;
101753             if (isFirstEdge) { startIndex = 0; }
101754             for (var i = startIndex; i < edgePts.length; i++) {
101755               this$1._pts.add(edgePts[i]);
101756             }
101757           } else {
101758             var startIndex$1 = edgePts.length - 2;
101759             if (isFirstEdge) { startIndex$1 = edgePts.length - 1; }
101760             for (var i$1 = startIndex$1; i$1 >= 0; i$1--) {
101761               this$1._pts.add(edgePts[i$1]);
101762             }
101763           }
101764         };
101765         EdgeRing.prototype.isHole = function isHole () {
101766           return this._isHole
101767         };
101768         EdgeRing.prototype.setInResult = function setInResult () {
101769           var de = this._startDe;
101770           do {
101771             de.getEdge().setInResult(true);
101772             de = de.getNext();
101773           } while (de !== this._startDe)
101774         };
101775         EdgeRing.prototype.containsPoint = function containsPoint (p) {
101776           var shell = this.getLinearRing();
101777           var env = shell.getEnvelopeInternal();
101778           if (!env.contains(p)) { return false }
101779           if (!CGAlgorithms.isPointInRing(p, shell.getCoordinates())) { return false }
101780           for (var i = this._holes.iterator(); i.hasNext();) {
101781             var hole = i.next();
101782             if (hole.containsPoint(p)) { return false }
101783           }
101784           return true
101785         };
101786         EdgeRing.prototype.addHole = function addHole (ring) {
101787           this._holes.add(ring);
101788         };
101789         EdgeRing.prototype.isShell = function isShell () {
101790           return this._shell === null
101791         };
101792         EdgeRing.prototype.getLabel = function getLabel () {
101793           return this._label
101794         };
101795         EdgeRing.prototype.getEdges = function getEdges () {
101796           return this._edges
101797         };
101798         EdgeRing.prototype.getMaxNodeDegree = function getMaxNodeDegree () {
101799           if (this._maxNodeDegree < 0) { this.computeMaxNodeDegree(); }
101800           return this._maxNodeDegree
101801         };
101802         EdgeRing.prototype.getShell = function getShell () {
101803           return this._shell
101804         };
101805         EdgeRing.prototype.mergeLabel = function mergeLabel () {
101806           if (arguments.length === 1) {
101807             var deLabel = arguments[0];
101808             this.mergeLabel(deLabel, 0);
101809             this.mergeLabel(deLabel, 1);
101810           } else if (arguments.length === 2) {
101811             var deLabel$1 = arguments[0];
101812             var geomIndex = arguments[1];
101813             var loc = deLabel$1.getLocation(geomIndex, Position.RIGHT);
101814             if (loc === Location.NONE) { return null }
101815             if (this._label.getLocation(geomIndex) === Location.NONE) {
101816               this._label.setLocation(geomIndex, loc);
101817               return null
101818             }
101819           }
101820         };
101821         EdgeRing.prototype.setShell = function setShell (shell) {
101822           this._shell = shell;
101823           if (shell !== null) { shell.addHole(this); }
101824         };
101825         EdgeRing.prototype.toPolygon = function toPolygon (geometryFactory) {
101826             var this$1 = this;
101827
101828           var holeLR = new Array(this._holes.size()).fill(null);
101829           for (var i = 0; i < this._holes.size(); i++) {
101830             holeLR[i] = this$1._holes.get(i).getLinearRing();
101831           }
101832           var poly = geometryFactory.createPolygon(this.getLinearRing(), holeLR);
101833           return poly
101834         };
101835         EdgeRing.prototype.interfaces_ = function interfaces_ () {
101836           return []
101837         };
101838         EdgeRing.prototype.getClass = function getClass () {
101839           return EdgeRing
101840         };
101841
101842         var MinimalEdgeRing = (function (EdgeRing$$1) {
101843           function MinimalEdgeRing () {
101844             var start = arguments[0];
101845             var geometryFactory = arguments[1];
101846             EdgeRing$$1.call(this, start, geometryFactory);
101847           }
101848
101849           if ( EdgeRing$$1 ) MinimalEdgeRing.__proto__ = EdgeRing$$1;
101850           MinimalEdgeRing.prototype = Object.create( EdgeRing$$1 && EdgeRing$$1.prototype );
101851           MinimalEdgeRing.prototype.constructor = MinimalEdgeRing;
101852           MinimalEdgeRing.prototype.setEdgeRing = function setEdgeRing (de, er) {
101853             de.setMinEdgeRing(er);
101854           };
101855           MinimalEdgeRing.prototype.getNext = function getNext (de) {
101856             return de.getNextMin()
101857           };
101858           MinimalEdgeRing.prototype.interfaces_ = function interfaces_ () {
101859             return []
101860           };
101861           MinimalEdgeRing.prototype.getClass = function getClass () {
101862             return MinimalEdgeRing
101863           };
101864
101865           return MinimalEdgeRing;
101866         }(EdgeRing));
101867
101868         var MaximalEdgeRing = (function (EdgeRing$$1) {
101869           function MaximalEdgeRing () {
101870             var start = arguments[0];
101871             var geometryFactory = arguments[1];
101872             EdgeRing$$1.call(this, start, geometryFactory);
101873           }
101874
101875           if ( EdgeRing$$1 ) MaximalEdgeRing.__proto__ = EdgeRing$$1;
101876           MaximalEdgeRing.prototype = Object.create( EdgeRing$$1 && EdgeRing$$1.prototype );
101877           MaximalEdgeRing.prototype.constructor = MaximalEdgeRing;
101878           MaximalEdgeRing.prototype.buildMinimalRings = function buildMinimalRings () {
101879             var this$1 = this;
101880
101881             var minEdgeRings = new ArrayList();
101882             var de = this._startDe;
101883             do {
101884               if (de.getMinEdgeRing() === null) {
101885                 var minEr = new MinimalEdgeRing(de, this$1._geometryFactory);
101886                 minEdgeRings.add(minEr);
101887               }
101888               de = de.getNext();
101889             } while (de !== this._startDe)
101890             return minEdgeRings
101891           };
101892           MaximalEdgeRing.prototype.setEdgeRing = function setEdgeRing (de, er) {
101893             de.setEdgeRing(er);
101894           };
101895           MaximalEdgeRing.prototype.linkDirectedEdgesForMinimalEdgeRings = function linkDirectedEdgesForMinimalEdgeRings () {
101896             var this$1 = this;
101897
101898             var de = this._startDe;
101899             do {
101900               var node = de.getNode();
101901               node.getEdges().linkMinimalDirectedEdges(this$1);
101902               de = de.getNext();
101903             } while (de !== this._startDe)
101904           };
101905           MaximalEdgeRing.prototype.getNext = function getNext (de) {
101906             return de.getNext()
101907           };
101908           MaximalEdgeRing.prototype.interfaces_ = function interfaces_ () {
101909             return []
101910           };
101911           MaximalEdgeRing.prototype.getClass = function getClass () {
101912             return MaximalEdgeRing
101913           };
101914
101915           return MaximalEdgeRing;
101916         }(EdgeRing));
101917
101918         var GraphComponent = function GraphComponent () {
101919           this._label = null;
101920           this._isInResult = false;
101921           this._isCovered = false;
101922           this._isCoveredSet = false;
101923           this._isVisited = false;
101924           if (arguments.length === 0) ; else if (arguments.length === 1) {
101925             var label = arguments[0];
101926             this._label = label;
101927           }
101928         };
101929         GraphComponent.prototype.setVisited = function setVisited (isVisited) {
101930           this._isVisited = isVisited;
101931         };
101932         GraphComponent.prototype.setInResult = function setInResult (isInResult) {
101933           this._isInResult = isInResult;
101934         };
101935         GraphComponent.prototype.isCovered = function isCovered () {
101936           return this._isCovered
101937         };
101938         GraphComponent.prototype.isCoveredSet = function isCoveredSet () {
101939           return this._isCoveredSet
101940         };
101941         GraphComponent.prototype.setLabel = function setLabel (label) {
101942           this._label = label;
101943         };
101944         GraphComponent.prototype.getLabel = function getLabel () {
101945           return this._label
101946         };
101947         GraphComponent.prototype.setCovered = function setCovered (isCovered) {
101948           this._isCovered = isCovered;
101949           this._isCoveredSet = true;
101950         };
101951         GraphComponent.prototype.updateIM = function updateIM (im) {
101952           Assert.isTrue(this._label.getGeometryCount() >= 2, 'found partial label');
101953           this.computeIM(im);
101954         };
101955         GraphComponent.prototype.isInResult = function isInResult () {
101956           return this._isInResult
101957         };
101958         GraphComponent.prototype.isVisited = function isVisited () {
101959           return this._isVisited
101960         };
101961         GraphComponent.prototype.interfaces_ = function interfaces_ () {
101962           return []
101963         };
101964         GraphComponent.prototype.getClass = function getClass () {
101965           return GraphComponent
101966         };
101967
101968         var Node$1 = (function (GraphComponent$$1) {
101969           function Node () {
101970             GraphComponent$$1.call(this);
101971             this._coord = null;
101972             this._edges = null;
101973             var coord = arguments[0];
101974             var edges = arguments[1];
101975             this._coord = coord;
101976             this._edges = edges;
101977             this._label = new Label(0, Location.NONE);
101978           }
101979
101980           if ( GraphComponent$$1 ) Node.__proto__ = GraphComponent$$1;
101981           Node.prototype = Object.create( GraphComponent$$1 && GraphComponent$$1.prototype );
101982           Node.prototype.constructor = Node;
101983           Node.prototype.isIncidentEdgeInResult = function isIncidentEdgeInResult () {
101984             for (var it = this.getEdges().getEdges().iterator(); it.hasNext();) {
101985               var de = it.next();
101986               if (de.getEdge().isInResult()) { return true }
101987             }
101988             return false
101989           };
101990           Node.prototype.isIsolated = function isIsolated () {
101991             return this._label.getGeometryCount() === 1
101992           };
101993           Node.prototype.getCoordinate = function getCoordinate () {
101994             return this._coord
101995           };
101996           Node.prototype.print = function print (out) {
101997             out.println('node ' + this._coord + ' lbl: ' + this._label);
101998           };
101999           Node.prototype.computeIM = function computeIM (im) {};
102000           Node.prototype.computeMergedLocation = function computeMergedLocation (label2, eltIndex) {
102001             var loc = Location.NONE;
102002             loc = this._label.getLocation(eltIndex);
102003             if (!label2.isNull(eltIndex)) {
102004               var nLoc = label2.getLocation(eltIndex);
102005               if (loc !== Location.BOUNDARY) { loc = nLoc; }
102006             }
102007             return loc
102008           };
102009           Node.prototype.setLabel = function setLabel () {
102010             if (arguments.length === 2) {
102011               var argIndex = arguments[0];
102012               var onLocation = arguments[1];
102013               if (this._label === null) {
102014                 this._label = new Label(argIndex, onLocation);
102015               } else { this._label.setLocation(argIndex, onLocation); }
102016             } else { return GraphComponent$$1.prototype.setLabel.apply(this, arguments) }
102017           };
102018           Node.prototype.getEdges = function getEdges () {
102019             return this._edges
102020           };
102021           Node.prototype.mergeLabel = function mergeLabel () {
102022             var this$1 = this;
102023
102024             if (arguments[0] instanceof Node) {
102025               var n = arguments[0];
102026               this.mergeLabel(n._label);
102027             } else if (arguments[0] instanceof Label) {
102028               var label2 = arguments[0];
102029               for (var i = 0; i < 2; i++) {
102030                 var loc = this$1.computeMergedLocation(label2, i);
102031                 var thisLoc = this$1._label.getLocation(i);
102032                 if (thisLoc === Location.NONE) { this$1._label.setLocation(i, loc); }
102033               }
102034             }
102035           };
102036           Node.prototype.add = function add (e) {
102037             this._edges.insert(e);
102038             e.setNode(this);
102039           };
102040           Node.prototype.setLabelBoundary = function setLabelBoundary (argIndex) {
102041             if (this._label === null) { return null }
102042             var loc = Location.NONE;
102043             if (this._label !== null) { loc = this._label.getLocation(argIndex); }
102044             var newLoc = null;
102045             switch (loc) {
102046               case Location.BOUNDARY:
102047                 newLoc = Location.INTERIOR;
102048                 break
102049               case Location.INTERIOR:
102050                 newLoc = Location.BOUNDARY;
102051                 break
102052               default:
102053                 newLoc = Location.BOUNDARY;
102054                 break
102055             }
102056             this._label.setLocation(argIndex, newLoc);
102057           };
102058           Node.prototype.interfaces_ = function interfaces_ () {
102059             return []
102060           };
102061           Node.prototype.getClass = function getClass () {
102062             return Node
102063           };
102064
102065           return Node;
102066         }(GraphComponent));
102067
102068         var NodeMap = function NodeMap () {
102069           this.nodeMap = new TreeMap();
102070           this.nodeFact = null;
102071           var nodeFact = arguments[0];
102072           this.nodeFact = nodeFact;
102073         };
102074         NodeMap.prototype.find = function find (coord) {
102075           return this.nodeMap.get(coord)
102076         };
102077         NodeMap.prototype.addNode = function addNode () {
102078           if (arguments[0] instanceof Coordinate) {
102079             var coord = arguments[0];
102080             var node = this.nodeMap.get(coord);
102081             if (node === null) {
102082               node = this.nodeFact.createNode(coord);
102083               this.nodeMap.put(coord, node);
102084             }
102085             return node
102086           } else if (arguments[0] instanceof Node$1) {
102087             var n = arguments[0];
102088             var node$1 = this.nodeMap.get(n.getCoordinate());
102089             if (node$1 === null) {
102090               this.nodeMap.put(n.getCoordinate(), n);
102091               return n
102092             }
102093             node$1.mergeLabel(n);
102094             return node$1
102095           }
102096         };
102097         NodeMap.prototype.print = function print (out) {
102098           for (var it = this.iterator(); it.hasNext();) {
102099             var n = it.next();
102100             n.print(out);
102101           }
102102         };
102103         NodeMap.prototype.iterator = function iterator () {
102104           return this.nodeMap.values().iterator()
102105         };
102106         NodeMap.prototype.values = function values () {
102107           return this.nodeMap.values()
102108         };
102109         NodeMap.prototype.getBoundaryNodes = function getBoundaryNodes (geomIndex) {
102110           var bdyNodes = new ArrayList();
102111           for (var i = this.iterator(); i.hasNext();) {
102112             var node = i.next();
102113             if (node.getLabel().getLocation(geomIndex) === Location.BOUNDARY) { bdyNodes.add(node); }
102114           }
102115           return bdyNodes
102116         };
102117         NodeMap.prototype.add = function add (e) {
102118           var p = e.getCoordinate();
102119           var n = this.addNode(p);
102120           n.add(e);
102121         };
102122         NodeMap.prototype.interfaces_ = function interfaces_ () {
102123           return []
102124         };
102125         NodeMap.prototype.getClass = function getClass () {
102126           return NodeMap
102127         };
102128
102129         var Quadrant = function Quadrant () {};
102130
102131         var staticAccessors$21 = { NE: { configurable: true },NW: { configurable: true },SW: { configurable: true },SE: { configurable: true } };
102132
102133         Quadrant.prototype.interfaces_ = function interfaces_ () {
102134           return []
102135         };
102136         Quadrant.prototype.getClass = function getClass () {
102137           return Quadrant
102138         };
102139         Quadrant.isNorthern = function isNorthern (quad) {
102140           return quad === Quadrant.NE || quad === Quadrant.NW
102141         };
102142         Quadrant.isOpposite = function isOpposite (quad1, quad2) {
102143           if (quad1 === quad2) { return false }
102144           var diff = (quad1 - quad2 + 4) % 4;
102145           if (diff === 2) { return true }
102146           return false
102147         };
102148         Quadrant.commonHalfPlane = function commonHalfPlane (quad1, quad2) {
102149           if (quad1 === quad2) { return quad1 }
102150           var diff = (quad1 - quad2 + 4) % 4;
102151           if (diff === 2) { return -1 }
102152           var min = quad1 < quad2 ? quad1 : quad2;
102153           var max = quad1 > quad2 ? quad1 : quad2;
102154           if (min === 0 && max === 3) { return 3 }
102155           return min
102156         };
102157         Quadrant.isInHalfPlane = function isInHalfPlane (quad, halfPlane) {
102158           if (halfPlane === Quadrant.SE) {
102159             return quad === Quadrant.SE || quad === Quadrant.SW
102160           }
102161           return quad === halfPlane || quad === halfPlane + 1
102162         };
102163         Quadrant.quadrant = function quadrant () {
102164           if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
102165             var dx = arguments[0];
102166             var dy = arguments[1];
102167             if (dx === 0.0 && dy === 0.0) { throw new IllegalArgumentException('Cannot compute the quadrant for point ( ' + dx + ', ' + dy + ' )') }
102168             if (dx >= 0.0) {
102169               if (dy >= 0.0) { return Quadrant.NE; } else { return Quadrant.SE }
102170             } else {
102171               if (dy >= 0.0) { return Quadrant.NW; } else { return Quadrant.SW }
102172             }
102173           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
102174             var p0 = arguments[0];
102175             var p1 = arguments[1];
102176             if (p1.x === p0.x && p1.y === p0.y) { throw new IllegalArgumentException('Cannot compute the quadrant for two identical points ' + p0) }
102177             if (p1.x >= p0.x) {
102178               if (p1.y >= p0.y) { return Quadrant.NE; } else { return Quadrant.SE }
102179             } else {
102180               if (p1.y >= p0.y) { return Quadrant.NW; } else { return Quadrant.SW }
102181             }
102182           }
102183         };
102184         staticAccessors$21.NE.get = function () { return 0 };
102185         staticAccessors$21.NW.get = function () { return 1 };
102186         staticAccessors$21.SW.get = function () { return 2 };
102187         staticAccessors$21.SE.get = function () { return 3 };
102188
102189         Object.defineProperties( Quadrant, staticAccessors$21 );
102190
102191         var EdgeEnd = function EdgeEnd () {
102192           this._edge = null;
102193           this._label = null;
102194           this._node = null;
102195           this._p0 = null;
102196           this._p1 = null;
102197           this._dx = null;
102198           this._dy = null;
102199           this._quadrant = null;
102200           if (arguments.length === 1) {
102201             var edge = arguments[0];
102202             this._edge = edge;
102203           } else if (arguments.length === 3) {
102204             var edge$1 = arguments[0];
102205             var p0 = arguments[1];
102206             var p1 = arguments[2];
102207             var label = null;
102208             this._edge = edge$1;
102209             this.init(p0, p1);
102210             this._label = label;
102211           } else if (arguments.length === 4) {
102212             var edge$2 = arguments[0];
102213             var p0$1 = arguments[1];
102214             var p1$1 = arguments[2];
102215             var label$1 = arguments[3];
102216             this._edge = edge$2;
102217             this.init(p0$1, p1$1);
102218             this._label = label$1;
102219           }
102220         };
102221         EdgeEnd.prototype.compareDirection = function compareDirection (e) {
102222           if (this._dx === e._dx && this._dy === e._dy) { return 0 }
102223           if (this._quadrant > e._quadrant) { return 1 }
102224           if (this._quadrant < e._quadrant) { return -1 }
102225           return CGAlgorithms.computeOrientation(e._p0, e._p1, this._p1)
102226         };
102227         EdgeEnd.prototype.getDy = function getDy () {
102228           return this._dy
102229         };
102230         EdgeEnd.prototype.getCoordinate = function getCoordinate () {
102231           return this._p0
102232         };
102233         EdgeEnd.prototype.setNode = function setNode (node) {
102234           this._node = node;
102235         };
102236         EdgeEnd.prototype.print = function print (out) {
102237           var angle = Math.atan2(this._dy, this._dx);
102238           var className = this.getClass().getName();
102239           var lastDotPos = className.lastIndexOf('.');
102240           var name = className.substring(lastDotPos + 1);
102241           out.print('  ' + name + ': ' + this._p0 + ' - ' + this._p1 + ' ' + this._quadrant + ':' + angle + '   ' + this._label);
102242         };
102243         EdgeEnd.prototype.compareTo = function compareTo (obj) {
102244           var e = obj;
102245           return this.compareDirection(e)
102246         };
102247         EdgeEnd.prototype.getDirectedCoordinate = function getDirectedCoordinate () {
102248           return this._p1
102249         };
102250         EdgeEnd.prototype.getDx = function getDx () {
102251           return this._dx
102252         };
102253         EdgeEnd.prototype.getLabel = function getLabel () {
102254           return this._label
102255         };
102256         EdgeEnd.prototype.getEdge = function getEdge () {
102257           return this._edge
102258         };
102259         EdgeEnd.prototype.getQuadrant = function getQuadrant () {
102260           return this._quadrant
102261         };
102262         EdgeEnd.prototype.getNode = function getNode () {
102263           return this._node
102264         };
102265         EdgeEnd.prototype.toString = function toString () {
102266           var angle = Math.atan2(this._dy, this._dx);
102267           var className = this.getClass().getName();
102268           var lastDotPos = className.lastIndexOf('.');
102269           var name = className.substring(lastDotPos + 1);
102270           return '  ' + name + ': ' + this._p0 + ' - ' + this._p1 + ' ' + this._quadrant + ':' + angle + '   ' + this._label
102271         };
102272         EdgeEnd.prototype.computeLabel = function computeLabel (boundaryNodeRule) {};
102273         EdgeEnd.prototype.init = function init (p0, p1) {
102274           this._p0 = p0;
102275           this._p1 = p1;
102276           this._dx = p1.x - p0.x;
102277           this._dy = p1.y - p0.y;
102278           this._quadrant = Quadrant.quadrant(this._dx, this._dy);
102279           Assert.isTrue(!(this._dx === 0 && this._dy === 0), 'EdgeEnd with identical endpoints found');
102280         };
102281         EdgeEnd.prototype.interfaces_ = function interfaces_ () {
102282           return [Comparable]
102283         };
102284         EdgeEnd.prototype.getClass = function getClass () {
102285           return EdgeEnd
102286         };
102287
102288         var DirectedEdge = (function (EdgeEnd$$1) {
102289           function DirectedEdge () {
102290             var edge = arguments[0];
102291             var isForward = arguments[1];
102292             EdgeEnd$$1.call(this, edge);
102293             this._isForward = null;
102294             this._isInResult = false;
102295             this._isVisited = false;
102296             this._sym = null;
102297             this._next = null;
102298             this._nextMin = null;
102299             this._edgeRing = null;
102300             this._minEdgeRing = null;
102301             this._depth = [0, -999, -999];
102302             this._isForward = isForward;
102303             if (isForward) {
102304               this.init(edge.getCoordinate(0), edge.getCoordinate(1));
102305             } else {
102306               var n = edge.getNumPoints() - 1;
102307               this.init(edge.getCoordinate(n), edge.getCoordinate(n - 1));
102308             }
102309             this.computeDirectedLabel();
102310           }
102311
102312           if ( EdgeEnd$$1 ) DirectedEdge.__proto__ = EdgeEnd$$1;
102313           DirectedEdge.prototype = Object.create( EdgeEnd$$1 && EdgeEnd$$1.prototype );
102314           DirectedEdge.prototype.constructor = DirectedEdge;
102315           DirectedEdge.prototype.getNextMin = function getNextMin () {
102316             return this._nextMin
102317           };
102318           DirectedEdge.prototype.getDepth = function getDepth (position) {
102319             return this._depth[position]
102320           };
102321           DirectedEdge.prototype.setVisited = function setVisited (isVisited) {
102322             this._isVisited = isVisited;
102323           };
102324           DirectedEdge.prototype.computeDirectedLabel = function computeDirectedLabel () {
102325             this._label = new Label(this._edge.getLabel());
102326             if (!this._isForward) { this._label.flip(); }
102327           };
102328           DirectedEdge.prototype.getNext = function getNext () {
102329             return this._next
102330           };
102331           DirectedEdge.prototype.setDepth = function setDepth (position, depthVal) {
102332             if (this._depth[position] !== -999) {
102333               if (this._depth[position] !== depthVal) { throw new TopologyException('assigned depths do not match', this.getCoordinate()) }
102334             }
102335             this._depth[position] = depthVal;
102336           };
102337           DirectedEdge.prototype.isInteriorAreaEdge = function isInteriorAreaEdge () {
102338             var this$1 = this;
102339
102340             var isInteriorAreaEdge = true;
102341             for (var i = 0; i < 2; i++) {
102342               if (!(this$1._label.isArea(i) && this$1._label.getLocation(i, Position.LEFT) === Location.INTERIOR && this$1._label.getLocation(i, Position.RIGHT) === Location.INTERIOR)) {
102343                 isInteriorAreaEdge = false;
102344               }
102345             }
102346             return isInteriorAreaEdge
102347           };
102348           DirectedEdge.prototype.setNextMin = function setNextMin (nextMin) {
102349             this._nextMin = nextMin;
102350           };
102351           DirectedEdge.prototype.print = function print (out) {
102352             EdgeEnd$$1.prototype.print.call(this, out);
102353             out.print(' ' + this._depth[Position.LEFT] + '/' + this._depth[Position.RIGHT]);
102354             out.print(' (' + this.getDepthDelta() + ')');
102355             if (this._isInResult) { out.print(' inResult'); }
102356           };
102357           DirectedEdge.prototype.setMinEdgeRing = function setMinEdgeRing (minEdgeRing) {
102358             this._minEdgeRing = minEdgeRing;
102359           };
102360           DirectedEdge.prototype.isLineEdge = function isLineEdge () {
102361             var isLine = this._label.isLine(0) || this._label.isLine(1);
102362             var isExteriorIfArea0 = !this._label.isArea(0) || this._label.allPositionsEqual(0, Location.EXTERIOR);
102363             var isExteriorIfArea1 = !this._label.isArea(1) || this._label.allPositionsEqual(1, Location.EXTERIOR);
102364             return isLine && isExteriorIfArea0 && isExteriorIfArea1
102365           };
102366           DirectedEdge.prototype.setEdgeRing = function setEdgeRing (edgeRing) {
102367             this._edgeRing = edgeRing;
102368           };
102369           DirectedEdge.prototype.getMinEdgeRing = function getMinEdgeRing () {
102370             return this._minEdgeRing
102371           };
102372           DirectedEdge.prototype.getDepthDelta = function getDepthDelta () {
102373             var depthDelta = this._edge.getDepthDelta();
102374             if (!this._isForward) { depthDelta = -depthDelta; }
102375             return depthDelta
102376           };
102377           DirectedEdge.prototype.setInResult = function setInResult (isInResult) {
102378             this._isInResult = isInResult;
102379           };
102380           DirectedEdge.prototype.getSym = function getSym () {
102381             return this._sym
102382           };
102383           DirectedEdge.prototype.isForward = function isForward () {
102384             return this._isForward
102385           };
102386           DirectedEdge.prototype.getEdge = function getEdge () {
102387             return this._edge
102388           };
102389           DirectedEdge.prototype.printEdge = function printEdge (out) {
102390             this.print(out);
102391             out.print(' ');
102392             if (this._isForward) { this._edge.print(out); } else { this._edge.printReverse(out); }
102393           };
102394           DirectedEdge.prototype.setSym = function setSym (de) {
102395             this._sym = de;
102396           };
102397           DirectedEdge.prototype.setVisitedEdge = function setVisitedEdge (isVisited) {
102398             this.setVisited(isVisited);
102399             this._sym.setVisited(isVisited);
102400           };
102401           DirectedEdge.prototype.setEdgeDepths = function setEdgeDepths (position, depth) {
102402             var depthDelta = this.getEdge().getDepthDelta();
102403             if (!this._isForward) { depthDelta = -depthDelta; }
102404             var directionFactor = 1;
102405             if (position === Position.LEFT) { directionFactor = -1; }
102406             var oppositePos = Position.opposite(position);
102407             var delta = depthDelta * directionFactor;
102408             var oppositeDepth = depth + delta;
102409             this.setDepth(position, depth);
102410             this.setDepth(oppositePos, oppositeDepth);
102411           };
102412           DirectedEdge.prototype.getEdgeRing = function getEdgeRing () {
102413             return this._edgeRing
102414           };
102415           DirectedEdge.prototype.isInResult = function isInResult () {
102416             return this._isInResult
102417           };
102418           DirectedEdge.prototype.setNext = function setNext (next) {
102419             this._next = next;
102420           };
102421           DirectedEdge.prototype.isVisited = function isVisited () {
102422             return this._isVisited
102423           };
102424           DirectedEdge.prototype.interfaces_ = function interfaces_ () {
102425             return []
102426           };
102427           DirectedEdge.prototype.getClass = function getClass () {
102428             return DirectedEdge
102429           };
102430           DirectedEdge.depthFactor = function depthFactor (currLocation, nextLocation) {
102431             if (currLocation === Location.EXTERIOR && nextLocation === Location.INTERIOR) { return 1; } else if (currLocation === Location.INTERIOR && nextLocation === Location.EXTERIOR) { return -1 }
102432             return 0
102433           };
102434
102435           return DirectedEdge;
102436         }(EdgeEnd));
102437
102438         var NodeFactory = function NodeFactory () {};
102439
102440         NodeFactory.prototype.createNode = function createNode (coord) {
102441           return new Node$1(coord, null)
102442         };
102443         NodeFactory.prototype.interfaces_ = function interfaces_ () {
102444           return []
102445         };
102446         NodeFactory.prototype.getClass = function getClass () {
102447           return NodeFactory
102448         };
102449
102450         var PlanarGraph = function PlanarGraph () {
102451           this._edges = new ArrayList();
102452           this._nodes = null;
102453           this._edgeEndList = new ArrayList();
102454           if (arguments.length === 0) {
102455             this._nodes = new NodeMap(new NodeFactory());
102456           } else if (arguments.length === 1) {
102457             var nodeFact = arguments[0];
102458             this._nodes = new NodeMap(nodeFact);
102459           }
102460         };
102461         PlanarGraph.prototype.printEdges = function printEdges (out) {
102462             var this$1 = this;
102463
102464           out.println('Edges:');
102465           for (var i = 0; i < this._edges.size(); i++) {
102466             out.println('edge ' + i + ':');
102467             var e = this$1._edges.get(i);
102468             e.print(out);
102469             e.eiList.print(out);
102470           }
102471         };
102472         PlanarGraph.prototype.find = function find (coord) {
102473           return this._nodes.find(coord)
102474         };
102475         PlanarGraph.prototype.addNode = function addNode () {
102476           if (arguments[0] instanceof Node$1) {
102477             var node = arguments[0];
102478             return this._nodes.addNode(node)
102479           } else if (arguments[0] instanceof Coordinate) {
102480             var coord = arguments[0];
102481             return this._nodes.addNode(coord)
102482           }
102483         };
102484         PlanarGraph.prototype.getNodeIterator = function getNodeIterator () {
102485           return this._nodes.iterator()
102486         };
102487         PlanarGraph.prototype.linkResultDirectedEdges = function linkResultDirectedEdges () {
102488           for (var nodeit = this._nodes.iterator(); nodeit.hasNext();) {
102489             var node = nodeit.next();
102490             node.getEdges().linkResultDirectedEdges();
102491           }
102492         };
102493         PlanarGraph.prototype.debugPrintln = function debugPrintln (o) {
102494           System.out.println(o);
102495         };
102496         PlanarGraph.prototype.isBoundaryNode = function isBoundaryNode (geomIndex, coord) {
102497           var node = this._nodes.find(coord);
102498           if (node === null) { return false }
102499           var label = node.getLabel();
102500           if (label !== null && label.getLocation(geomIndex) === Location.BOUNDARY) { return true }
102501           return false
102502         };
102503         PlanarGraph.prototype.linkAllDirectedEdges = function linkAllDirectedEdges () {
102504           for (var nodeit = this._nodes.iterator(); nodeit.hasNext();) {
102505             var node = nodeit.next();
102506             node.getEdges().linkAllDirectedEdges();
102507           }
102508         };
102509         PlanarGraph.prototype.matchInSameDirection = function matchInSameDirection (p0, p1, ep0, ep1) {
102510           if (!p0.equals(ep0)) { return false }
102511           if (CGAlgorithms.computeOrientation(p0, p1, ep1) === CGAlgorithms.COLLINEAR && Quadrant.quadrant(p0, p1) === Quadrant.quadrant(ep0, ep1)) { return true }
102512           return false
102513         };
102514         PlanarGraph.prototype.getEdgeEnds = function getEdgeEnds () {
102515           return this._edgeEndList
102516         };
102517         PlanarGraph.prototype.debugPrint = function debugPrint (o) {
102518           System.out.print(o);
102519         };
102520         PlanarGraph.prototype.getEdgeIterator = function getEdgeIterator () {
102521           return this._edges.iterator()
102522         };
102523         PlanarGraph.prototype.findEdgeInSameDirection = function findEdgeInSameDirection (p0, p1) {
102524             var this$1 = this;
102525
102526           for (var i = 0; i < this._edges.size(); i++) {
102527             var e = this$1._edges.get(i);
102528             var eCoord = e.getCoordinates();
102529             if (this$1.matchInSameDirection(p0, p1, eCoord[0], eCoord[1])) { return e }
102530             if (this$1.matchInSameDirection(p0, p1, eCoord[eCoord.length - 1], eCoord[eCoord.length - 2])) { return e }
102531           }
102532           return null
102533         };
102534         PlanarGraph.prototype.insertEdge = function insertEdge (e) {
102535           this._edges.add(e);
102536         };
102537         PlanarGraph.prototype.findEdgeEnd = function findEdgeEnd (e) {
102538           for (var i = this.getEdgeEnds().iterator(); i.hasNext();) {
102539             var ee = i.next();
102540             if (ee.getEdge() === e) { return ee }
102541           }
102542           return null
102543         };
102544         PlanarGraph.prototype.addEdges = function addEdges (edgesToAdd) {
102545             var this$1 = this;
102546
102547           for (var it = edgesToAdd.iterator(); it.hasNext();) {
102548             var e = it.next();
102549             this$1._edges.add(e);
102550             var de1 = new DirectedEdge(e, true);
102551             var de2 = new DirectedEdge(e, false);
102552             de1.setSym(de2);
102553             de2.setSym(de1);
102554             this$1.add(de1);
102555             this$1.add(de2);
102556           }
102557         };
102558         PlanarGraph.prototype.add = function add (e) {
102559           this._nodes.add(e);
102560           this._edgeEndList.add(e);
102561         };
102562         PlanarGraph.prototype.getNodes = function getNodes () {
102563           return this._nodes.values()
102564         };
102565         PlanarGraph.prototype.findEdge = function findEdge (p0, p1) {
102566             var this$1 = this;
102567
102568           for (var i = 0; i < this._edges.size(); i++) {
102569             var e = this$1._edges.get(i);
102570             var eCoord = e.getCoordinates();
102571             if (p0.equals(eCoord[0]) && p1.equals(eCoord[1])) { return e }
102572           }
102573           return null
102574         };
102575         PlanarGraph.prototype.interfaces_ = function interfaces_ () {
102576           return []
102577         };
102578         PlanarGraph.prototype.getClass = function getClass () {
102579           return PlanarGraph
102580         };
102581         PlanarGraph.linkResultDirectedEdges = function linkResultDirectedEdges (nodes) {
102582           for (var nodeit = nodes.iterator(); nodeit.hasNext();) {
102583             var node = nodeit.next();
102584             node.getEdges().linkResultDirectedEdges();
102585           }
102586         };
102587
102588         var PolygonBuilder = function PolygonBuilder () {
102589           this._geometryFactory = null;
102590           this._shellList = new ArrayList();
102591           var geometryFactory = arguments[0];
102592           this._geometryFactory = geometryFactory;
102593         };
102594         PolygonBuilder.prototype.sortShellsAndHoles = function sortShellsAndHoles (edgeRings, shellList, freeHoleList) {
102595           for (var it = edgeRings.iterator(); it.hasNext();) {
102596             var er = it.next();
102597             if (er.isHole()) {
102598               freeHoleList.add(er);
102599             } else {
102600               shellList.add(er);
102601             }
102602           }
102603         };
102604         PolygonBuilder.prototype.computePolygons = function computePolygons (shellList) {
102605             var this$1 = this;
102606
102607           var resultPolyList = new ArrayList();
102608           for (var it = shellList.iterator(); it.hasNext();) {
102609             var er = it.next();
102610             var poly = er.toPolygon(this$1._geometryFactory);
102611             resultPolyList.add(poly);
102612           }
102613           return resultPolyList
102614         };
102615         PolygonBuilder.prototype.placeFreeHoles = function placeFreeHoles (shellList, freeHoleList) {
102616             var this$1 = this;
102617
102618           for (var it = freeHoleList.iterator(); it.hasNext();) {
102619             var hole = it.next();
102620             if (hole.getShell() === null) {
102621               var shell = this$1.findEdgeRingContaining(hole, shellList);
102622               if (shell === null) { throw new TopologyException('unable to assign hole to a shell', hole.getCoordinate(0)) }
102623               hole.setShell(shell);
102624             }
102625           }
102626         };
102627         PolygonBuilder.prototype.buildMinimalEdgeRings = function buildMinimalEdgeRings (maxEdgeRings, shellList, freeHoleList) {
102628             var this$1 = this;
102629
102630           var edgeRings = new ArrayList();
102631           for (var it = maxEdgeRings.iterator(); it.hasNext();) {
102632             var er = it.next();
102633             if (er.getMaxNodeDegree() > 2) {
102634               er.linkDirectedEdgesForMinimalEdgeRings();
102635               var minEdgeRings = er.buildMinimalRings();
102636               var shell = this$1.findShell(minEdgeRings);
102637               if (shell !== null) {
102638                 this$1.placePolygonHoles(shell, minEdgeRings);
102639                 shellList.add(shell);
102640               } else {
102641                 freeHoleList.addAll(minEdgeRings);
102642               }
102643             } else {
102644               edgeRings.add(er);
102645             }
102646           }
102647           return edgeRings
102648         };
102649         PolygonBuilder.prototype.containsPoint = function containsPoint (p) {
102650           for (var it = this._shellList.iterator(); it.hasNext();) {
102651             var er = it.next();
102652             if (er.containsPoint(p)) { return true }
102653           }
102654           return false
102655         };
102656         PolygonBuilder.prototype.buildMaximalEdgeRings = function buildMaximalEdgeRings (dirEdges) {
102657             var this$1 = this;
102658
102659           var maxEdgeRings = new ArrayList();
102660           for (var it = dirEdges.iterator(); it.hasNext();) {
102661             var de = it.next();
102662             if (de.isInResult() && de.getLabel().isArea()) {
102663               if (de.getEdgeRing() === null) {
102664                 var er = new MaximalEdgeRing(de, this$1._geometryFactory);
102665                 maxEdgeRings.add(er);
102666                 er.setInResult();
102667               }
102668             }
102669           }
102670           return maxEdgeRings
102671         };
102672         PolygonBuilder.prototype.placePolygonHoles = function placePolygonHoles (shell, minEdgeRings) {
102673           for (var it = minEdgeRings.iterator(); it.hasNext();) {
102674             var er = it.next();
102675             if (er.isHole()) {
102676               er.setShell(shell);
102677             }
102678           }
102679         };
102680         PolygonBuilder.prototype.getPolygons = function getPolygons () {
102681           var resultPolyList = this.computePolygons(this._shellList);
102682           return resultPolyList
102683         };
102684         PolygonBuilder.prototype.findEdgeRingContaining = function findEdgeRingContaining (testEr, shellList) {
102685           var testRing = testEr.getLinearRing();
102686           var testEnv = testRing.getEnvelopeInternal();
102687           var testPt = testRing.getCoordinateN(0);
102688           var minShell = null;
102689           var minEnv = null;
102690           for (var it = shellList.iterator(); it.hasNext();) {
102691             var tryShell = it.next();
102692             var tryRing = tryShell.getLinearRing();
102693             var tryEnv = tryRing.getEnvelopeInternal();
102694             if (minShell !== null) { minEnv = minShell.getLinearRing().getEnvelopeInternal(); }
102695             var isContained = false;
102696             if (tryEnv.contains(testEnv) && CGAlgorithms.isPointInRing(testPt, tryRing.getCoordinates())) { isContained = true; }
102697             if (isContained) {
102698               if (minShell === null || minEnv.contains(tryEnv)) {
102699                 minShell = tryShell;
102700               }
102701             }
102702           }
102703           return minShell
102704         };
102705         PolygonBuilder.prototype.findShell = function findShell (minEdgeRings) {
102706           var shellCount = 0;
102707           var shell = null;
102708           for (var it = minEdgeRings.iterator(); it.hasNext();) {
102709             var er = it.next();
102710             if (!er.isHole()) {
102711               shell = er;
102712               shellCount++;
102713             }
102714           }
102715           Assert.isTrue(shellCount <= 1, 'found two shells in MinimalEdgeRing list');
102716           return shell
102717         };
102718         PolygonBuilder.prototype.add = function add () {
102719           if (arguments.length === 1) {
102720             var graph = arguments[0];
102721             this.add(graph.getEdgeEnds(), graph.getNodes());
102722           } else if (arguments.length === 2) {
102723             var dirEdges = arguments[0];
102724             var nodes = arguments[1];
102725             PlanarGraph.linkResultDirectedEdges(nodes);
102726             var maxEdgeRings = this.buildMaximalEdgeRings(dirEdges);
102727             var freeHoleList = new ArrayList();
102728             var edgeRings = this.buildMinimalEdgeRings(maxEdgeRings, this._shellList, freeHoleList);
102729             this.sortShellsAndHoles(edgeRings, this._shellList, freeHoleList);
102730             this.placeFreeHoles(this._shellList, freeHoleList);
102731           }
102732         };
102733         PolygonBuilder.prototype.interfaces_ = function interfaces_ () {
102734           return []
102735         };
102736         PolygonBuilder.prototype.getClass = function getClass () {
102737           return PolygonBuilder
102738         };
102739
102740         var Boundable = function Boundable () {};
102741
102742         Boundable.prototype.getBounds = function getBounds () {};
102743         Boundable.prototype.interfaces_ = function interfaces_ () {
102744           return []
102745         };
102746         Boundable.prototype.getClass = function getClass () {
102747           return Boundable
102748         };
102749
102750         var ItemBoundable = function ItemBoundable () {
102751           this._bounds = null;
102752           this._item = null;
102753           var bounds = arguments[0];
102754           var item = arguments[1];
102755           this._bounds = bounds;
102756           this._item = item;
102757         };
102758         ItemBoundable.prototype.getItem = function getItem () {
102759           return this._item
102760         };
102761         ItemBoundable.prototype.getBounds = function getBounds () {
102762           return this._bounds
102763         };
102764         ItemBoundable.prototype.interfaces_ = function interfaces_ () {
102765           return [Boundable, Serializable]
102766         };
102767         ItemBoundable.prototype.getClass = function getClass () {
102768           return ItemBoundable
102769         };
102770
102771         var PriorityQueue = function PriorityQueue () {
102772           this._size = null;
102773           this._items = null;
102774           this._size = 0;
102775           this._items = new ArrayList();
102776           this._items.add(null);
102777         };
102778         PriorityQueue.prototype.poll = function poll () {
102779           if (this.isEmpty()) { return null }
102780           var minItem = this._items.get(1);
102781           this._items.set(1, this._items.get(this._size));
102782           this._size -= 1;
102783           this.reorder(1);
102784           return minItem
102785         };
102786         PriorityQueue.prototype.size = function size () {
102787           return this._size
102788         };
102789         PriorityQueue.prototype.reorder = function reorder (hole) {
102790             var this$1 = this;
102791
102792           var child = null;
102793           var tmp = this._items.get(hole);
102794           for (; hole * 2 <= this._size; hole = child) {
102795             child = hole * 2;
102796             if (child !== this$1._size && this$1._items.get(child + 1).compareTo(this$1._items.get(child)) < 0) { child++; }
102797             if (this$1._items.get(child).compareTo(tmp) < 0) { this$1._items.set(hole, this$1._items.get(child)); } else { break }
102798           }
102799           this._items.set(hole, tmp);
102800         };
102801         PriorityQueue.prototype.clear = function clear () {
102802           this._size = 0;
102803           this._items.clear();
102804         };
102805         PriorityQueue.prototype.isEmpty = function isEmpty () {
102806           return this._size === 0
102807         };
102808         PriorityQueue.prototype.add = function add (x) {
102809             var this$1 = this;
102810
102811           this._items.add(null);
102812           this._size += 1;
102813           var hole = this._size;
102814           this._items.set(0, x);
102815           for (; x.compareTo(this._items.get(Math.trunc(hole / 2))) < 0; hole /= 2) {
102816             this$1._items.set(hole, this$1._items.get(Math.trunc(hole / 2)));
102817           }
102818           this._items.set(hole, x);
102819         };
102820         PriorityQueue.prototype.interfaces_ = function interfaces_ () {
102821           return []
102822         };
102823         PriorityQueue.prototype.getClass = function getClass () {
102824           return PriorityQueue
102825         };
102826
102827         var ItemVisitor = function ItemVisitor () {};
102828
102829         ItemVisitor.prototype.visitItem = function visitItem (item) {};
102830         ItemVisitor.prototype.interfaces_ = function interfaces_ () {
102831           return []
102832         };
102833         ItemVisitor.prototype.getClass = function getClass () {
102834           return ItemVisitor
102835         };
102836
102837         var SpatialIndex = function SpatialIndex () {};
102838
102839         SpatialIndex.prototype.insert = function insert (itemEnv, item) {};
102840         SpatialIndex.prototype.remove = function remove (itemEnv, item) {};
102841         SpatialIndex.prototype.query = function query () {
102842           // if (arguments.length === 1) {
102843           // const searchEnv = arguments[0]
102844           // } else if (arguments.length === 2) {
102845           // const searchEnv = arguments[0]
102846           // const visitor = arguments[1]
102847           // }
102848         };
102849         SpatialIndex.prototype.interfaces_ = function interfaces_ () {
102850           return []
102851         };
102852         SpatialIndex.prototype.getClass = function getClass () {
102853           return SpatialIndex
102854         };
102855
102856         var AbstractNode = function AbstractNode () {
102857           this._childBoundables = new ArrayList();
102858           this._bounds = null;
102859           this._level = null;
102860           if (arguments.length === 0) ; else if (arguments.length === 1) {
102861             var level = arguments[0];
102862             this._level = level;
102863           }
102864         };
102865
102866         var staticAccessors$22 = { serialVersionUID: { configurable: true } };
102867         AbstractNode.prototype.getLevel = function getLevel () {
102868           return this._level
102869         };
102870         AbstractNode.prototype.size = function size () {
102871           return this._childBoundables.size()
102872         };
102873         AbstractNode.prototype.getChildBoundables = function getChildBoundables () {
102874           return this._childBoundables
102875         };
102876         AbstractNode.prototype.addChildBoundable = function addChildBoundable (childBoundable) {
102877           Assert.isTrue(this._bounds === null);
102878           this._childBoundables.add(childBoundable);
102879         };
102880         AbstractNode.prototype.isEmpty = function isEmpty () {
102881           return this._childBoundables.isEmpty()
102882         };
102883         AbstractNode.prototype.getBounds = function getBounds () {
102884           if (this._bounds === null) {
102885             this._bounds = this.computeBounds();
102886           }
102887           return this._bounds
102888         };
102889         AbstractNode.prototype.interfaces_ = function interfaces_ () {
102890           return [Boundable, Serializable]
102891         };
102892         AbstractNode.prototype.getClass = function getClass () {
102893           return AbstractNode
102894         };
102895         staticAccessors$22.serialVersionUID.get = function () { return 6493722185909573708 };
102896
102897         Object.defineProperties( AbstractNode, staticAccessors$22 );
102898
102899         var Collections = function Collections () {};
102900
102901         Collections.reverseOrder = function reverseOrder () {
102902           return {
102903             compare: function compare (a, b) {
102904               return b.compareTo(a)
102905             }
102906           }
102907         };
102908         Collections.min = function min (l) {
102909           Collections.sort(l);
102910           return l.get(0)
102911         };
102912         Collections.sort = function sort (l, c) {
102913           var a = l.toArray();
102914           if (c) {
102915             Arrays.sort(a, c);
102916           } else {
102917             Arrays.sort(a);
102918           }
102919           var i = l.iterator();
102920           for (var pos = 0, alen = a.length; pos < alen; pos++) {
102921             i.next();
102922             i.set(a[pos]);
102923           }
102924         };
102925         Collections.singletonList = function singletonList (o) {
102926           var arrayList = new ArrayList();
102927           arrayList.add(o);
102928           return arrayList
102929         };
102930
102931         var BoundablePair = function BoundablePair () {
102932           this._boundable1 = null;
102933           this._boundable2 = null;
102934           this._distance = null;
102935           this._itemDistance = null;
102936           var boundable1 = arguments[0];
102937           var boundable2 = arguments[1];
102938           var itemDistance = arguments[2];
102939           this._boundable1 = boundable1;
102940           this._boundable2 = boundable2;
102941           this._itemDistance = itemDistance;
102942           this._distance = this.distance();
102943         };
102944         BoundablePair.prototype.expandToQueue = function expandToQueue (priQ, minDistance) {
102945           var isComp1 = BoundablePair.isComposite(this._boundable1);
102946           var isComp2 = BoundablePair.isComposite(this._boundable2);
102947           if (isComp1 && isComp2) {
102948             if (BoundablePair.area(this._boundable1) > BoundablePair.area(this._boundable2)) {
102949               this.expand(this._boundable1, this._boundable2, priQ, minDistance);
102950               return null
102951             } else {
102952               this.expand(this._boundable2, this._boundable1, priQ, minDistance);
102953               return null
102954             }
102955           } else if (isComp1) {
102956             this.expand(this._boundable1, this._boundable2, priQ, minDistance);
102957             return null
102958           } else if (isComp2) {
102959             this.expand(this._boundable2, this._boundable1, priQ, minDistance);
102960             return null
102961           }
102962           throw new IllegalArgumentException('neither boundable is composite')
102963         };
102964         BoundablePair.prototype.isLeaves = function isLeaves () {
102965           return !(BoundablePair.isComposite(this._boundable1) || BoundablePair.isComposite(this._boundable2))
102966         };
102967         BoundablePair.prototype.compareTo = function compareTo (o) {
102968           var nd = o;
102969           if (this._distance < nd._distance) { return -1 }
102970           if (this._distance > nd._distance) { return 1 }
102971           return 0
102972         };
102973         BoundablePair.prototype.expand = function expand (bndComposite, bndOther, priQ, minDistance) {
102974             var this$1 = this;
102975
102976           var children = bndComposite.getChildBoundables();
102977           for (var i = children.iterator(); i.hasNext();) {
102978             var child = i.next();
102979             var bp = new BoundablePair(child, bndOther, this$1._itemDistance);
102980             if (bp.getDistance() < minDistance) {
102981               priQ.add(bp);
102982             }
102983           }
102984         };
102985         BoundablePair.prototype.getBoundable = function getBoundable (i) {
102986           if (i === 0) { return this._boundable1 }
102987           return this._boundable2
102988         };
102989         BoundablePair.prototype.getDistance = function getDistance () {
102990           return this._distance
102991         };
102992         BoundablePair.prototype.distance = function distance () {
102993           if (this.isLeaves()) {
102994             return this._itemDistance.distance(this._boundable1, this._boundable2)
102995           }
102996           return this._boundable1.getBounds().distance(this._boundable2.getBounds())
102997         };
102998         BoundablePair.prototype.interfaces_ = function interfaces_ () {
102999           return [Comparable]
103000         };
103001         BoundablePair.prototype.getClass = function getClass () {
103002           return BoundablePair
103003         };
103004         BoundablePair.area = function area (b) {
103005           return b.getBounds().getArea()
103006         };
103007         BoundablePair.isComposite = function isComposite (item) {
103008           return item instanceof AbstractNode
103009         };
103010
103011         var AbstractSTRtree = function AbstractSTRtree () {
103012           this._root = null;
103013           this._built = false;
103014           this._itemBoundables = new ArrayList();
103015           this._nodeCapacity = null;
103016           if (arguments.length === 0) {
103017             var nodeCapacity = AbstractSTRtree.DEFAULT_NODE_CAPACITY;
103018             this._nodeCapacity = nodeCapacity;
103019           } else if (arguments.length === 1) {
103020             var nodeCapacity$1 = arguments[0];
103021             Assert.isTrue(nodeCapacity$1 > 1, 'Node capacity must be greater than 1');
103022             this._nodeCapacity = nodeCapacity$1;
103023           }
103024         };
103025
103026         var staticAccessors$23 = { IntersectsOp: { configurable: true },serialVersionUID: { configurable: true },DEFAULT_NODE_CAPACITY: { configurable: true } };
103027         AbstractSTRtree.prototype.getNodeCapacity = function getNodeCapacity () {
103028           return this._nodeCapacity
103029         };
103030         AbstractSTRtree.prototype.lastNode = function lastNode (nodes) {
103031           return nodes.get(nodes.size() - 1)
103032         };
103033         AbstractSTRtree.prototype.size = function size () {
103034             var this$1 = this;
103035
103036           if (arguments.length === 0) {
103037             if (this.isEmpty()) {
103038               return 0
103039             }
103040             this.build();
103041             return this.size(this._root)
103042           } else if (arguments.length === 1) {
103043             var node = arguments[0];
103044             var size = 0;
103045             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103046               var childBoundable = i.next();
103047               if (childBoundable instanceof AbstractNode) {
103048                 size += this$1.size(childBoundable);
103049               } else if (childBoundable instanceof ItemBoundable) {
103050                 size += 1;
103051               }
103052             }
103053             return size
103054           }
103055         };
103056         AbstractSTRtree.prototype.removeItem = function removeItem (node, item) {
103057           var childToRemove = null;
103058           for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103059             var childBoundable = i.next();
103060             if (childBoundable instanceof ItemBoundable) {
103061               if (childBoundable.getItem() === item) { childToRemove = childBoundable; }
103062             }
103063           }
103064           if (childToRemove !== null) {
103065             node.getChildBoundables().remove(childToRemove);
103066             return true
103067           }
103068           return false
103069         };
103070         AbstractSTRtree.prototype.itemsTree = function itemsTree () {
103071             var this$1 = this;
103072
103073           if (arguments.length === 0) {
103074             this.build();
103075             var valuesTree = this.itemsTree(this._root);
103076             if (valuesTree === null) { return new ArrayList() }
103077             return valuesTree
103078           } else if (arguments.length === 1) {
103079             var node = arguments[0];
103080             var valuesTreeForNode = new ArrayList();
103081             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103082               var childBoundable = i.next();
103083               if (childBoundable instanceof AbstractNode) {
103084                 var valuesTreeForChild = this$1.itemsTree(childBoundable);
103085                 if (valuesTreeForChild !== null) { valuesTreeForNode.add(valuesTreeForChild); }
103086               } else if (childBoundable instanceof ItemBoundable) {
103087                 valuesTreeForNode.add(childBoundable.getItem());
103088               } else {
103089                 Assert.shouldNeverReachHere();
103090               }
103091             }
103092             if (valuesTreeForNode.size() <= 0) { return null }
103093             return valuesTreeForNode
103094           }
103095         };
103096         AbstractSTRtree.prototype.insert = function insert (bounds, item) {
103097           Assert.isTrue(!this._built, 'Cannot insert items into an STR packed R-tree after it has been built.');
103098           this._itemBoundables.add(new ItemBoundable(bounds, item));
103099         };
103100         AbstractSTRtree.prototype.boundablesAtLevel = function boundablesAtLevel () {
103101             var this$1 = this;
103102
103103           if (arguments.length === 1) {
103104             var level = arguments[0];
103105             var boundables = new ArrayList();
103106             this.boundablesAtLevel(level, this._root, boundables);
103107             return boundables
103108           } else if (arguments.length === 3) {
103109             var level$1 = arguments[0];
103110             var top = arguments[1];
103111             var boundables$1 = arguments[2];
103112             Assert.isTrue(level$1 > -2);
103113             if (top.getLevel() === level$1) {
103114               boundables$1.add(top);
103115               return null
103116             }
103117             for (var i = top.getChildBoundables().iterator(); i.hasNext();) {
103118               var boundable = i.next();
103119               if (boundable instanceof AbstractNode) {
103120                 this$1.boundablesAtLevel(level$1, boundable, boundables$1);
103121               } else {
103122                 Assert.isTrue(boundable instanceof ItemBoundable);
103123                 if (level$1 === -1) {
103124                   boundables$1.add(boundable);
103125                 }
103126               }
103127             }
103128             return null
103129           }
103130         };
103131         AbstractSTRtree.prototype.query = function query () {
103132             var this$1 = this;
103133
103134           if (arguments.length === 1) {
103135             var searchBounds = arguments[0];
103136             this.build();
103137             var matches = new ArrayList();
103138             if (this.isEmpty()) {
103139               return matches
103140             }
103141             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds)) {
103142               this.query(searchBounds, this._root, matches);
103143             }
103144             return matches
103145           } else if (arguments.length === 2) {
103146             var searchBounds$1 = arguments[0];
103147             var visitor = arguments[1];
103148             this.build();
103149             if (this.isEmpty()) {
103150               return null
103151             }
103152             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds$1)) {
103153               this.query(searchBounds$1, this._root, visitor);
103154             }
103155           } else if (arguments.length === 3) {
103156             if (hasInterface(arguments[2], ItemVisitor) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103157               var searchBounds$2 = arguments[0];
103158               var node = arguments[1];
103159               var visitor$1 = arguments[2];
103160               var childBoundables = node.getChildBoundables();
103161               for (var i = 0; i < childBoundables.size(); i++) {
103162                 var childBoundable = childBoundables.get(i);
103163                 if (!this$1.getIntersectsOp().intersects(childBoundable.getBounds(), searchBounds$2)) {
103164                   continue
103165                 }
103166                 if (childBoundable instanceof AbstractNode) {
103167                   this$1.query(searchBounds$2, childBoundable, visitor$1);
103168                 } else if (childBoundable instanceof ItemBoundable) {
103169                   visitor$1.visitItem(childBoundable.getItem());
103170                 } else {
103171                   Assert.shouldNeverReachHere();
103172                 }
103173               }
103174             } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103175               var searchBounds$3 = arguments[0];
103176               var node$1 = arguments[1];
103177               var matches$1 = arguments[2];
103178               var childBoundables$1 = node$1.getChildBoundables();
103179               for (var i$1 = 0; i$1 < childBoundables$1.size(); i$1++) {
103180                 var childBoundable$1 = childBoundables$1.get(i$1);
103181                 if (!this$1.getIntersectsOp().intersects(childBoundable$1.getBounds(), searchBounds$3)) {
103182                   continue
103183                 }
103184                 if (childBoundable$1 instanceof AbstractNode) {
103185                   this$1.query(searchBounds$3, childBoundable$1, matches$1);
103186                 } else if (childBoundable$1 instanceof ItemBoundable) {
103187                   matches$1.add(childBoundable$1.getItem());
103188                 } else {
103189                   Assert.shouldNeverReachHere();
103190                 }
103191               }
103192             }
103193           }
103194         };
103195         AbstractSTRtree.prototype.build = function build () {
103196           if (this._built) { return null }
103197           this._root = this._itemBoundables.isEmpty() ? this.createNode(0) : this.createHigherLevels(this._itemBoundables, -1);
103198           this._itemBoundables = null;
103199           this._built = true;
103200         };
103201         AbstractSTRtree.prototype.getRoot = function getRoot () {
103202           this.build();
103203           return this._root
103204         };
103205         AbstractSTRtree.prototype.remove = function remove () {
103206             var this$1 = this;
103207
103208           if (arguments.length === 2) {
103209             var searchBounds = arguments[0];
103210             var item = arguments[1];
103211             this.build();
103212             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds)) {
103213               return this.remove(searchBounds, this._root, item)
103214             }
103215             return false
103216           } else if (arguments.length === 3) {
103217             var searchBounds$1 = arguments[0];
103218             var node = arguments[1];
103219             var item$1 = arguments[2];
103220             var found = this.removeItem(node, item$1);
103221             if (found) { return true }
103222             var childToPrune = null;
103223             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103224               var childBoundable = i.next();
103225               if (!this$1.getIntersectsOp().intersects(childBoundable.getBounds(), searchBounds$1)) {
103226                 continue
103227               }
103228               if (childBoundable instanceof AbstractNode) {
103229                 found = this$1.remove(searchBounds$1, childBoundable, item$1);
103230                 if (found) {
103231                   childToPrune = childBoundable;
103232                   break
103233                 }
103234               }
103235             }
103236             if (childToPrune !== null) {
103237               if (childToPrune.getChildBoundables().isEmpty()) {
103238                 node.getChildBoundables().remove(childToPrune);
103239               }
103240             }
103241             return found
103242           }
103243         };
103244         AbstractSTRtree.prototype.createHigherLevels = function createHigherLevels (boundablesOfALevel, level) {
103245           Assert.isTrue(!boundablesOfALevel.isEmpty());
103246           var parentBoundables = this.createParentBoundables(boundablesOfALevel, level + 1);
103247           if (parentBoundables.size() === 1) {
103248             return parentBoundables.get(0)
103249           }
103250           return this.createHigherLevels(parentBoundables, level + 1)
103251         };
103252         AbstractSTRtree.prototype.depth = function depth () {
103253             var this$1 = this;
103254
103255           if (arguments.length === 0) {
103256             if (this.isEmpty()) {
103257               return 0
103258             }
103259             this.build();
103260             return this.depth(this._root)
103261           } else if (arguments.length === 1) {
103262             var node = arguments[0];
103263             var maxChildDepth = 0;
103264             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103265               var childBoundable = i.next();
103266               if (childBoundable instanceof AbstractNode) {
103267                 var childDepth = this$1.depth(childBoundable);
103268                 if (childDepth > maxChildDepth) { maxChildDepth = childDepth; }
103269               }
103270             }
103271             return maxChildDepth + 1
103272           }
103273         };
103274         AbstractSTRtree.prototype.createParentBoundables = function createParentBoundables (childBoundables, newLevel) {
103275             var this$1 = this;
103276
103277           Assert.isTrue(!childBoundables.isEmpty());
103278           var parentBoundables = new ArrayList();
103279           parentBoundables.add(this.createNode(newLevel));
103280           var sortedChildBoundables = new ArrayList(childBoundables);
103281           Collections.sort(sortedChildBoundables, this.getComparator());
103282           for (var i = sortedChildBoundables.iterator(); i.hasNext();) {
103283             var childBoundable = i.next();
103284             if (this$1.lastNode(parentBoundables).getChildBoundables().size() === this$1.getNodeCapacity()) {
103285               parentBoundables.add(this$1.createNode(newLevel));
103286             }
103287             this$1.lastNode(parentBoundables).addChildBoundable(childBoundable);
103288           }
103289           return parentBoundables
103290         };
103291         AbstractSTRtree.prototype.isEmpty = function isEmpty () {
103292           if (!this._built) { return this._itemBoundables.isEmpty() }
103293           return this._root.isEmpty()
103294         };
103295         AbstractSTRtree.prototype.interfaces_ = function interfaces_ () {
103296           return [Serializable]
103297         };
103298         AbstractSTRtree.prototype.getClass = function getClass () {
103299           return AbstractSTRtree
103300         };
103301         AbstractSTRtree.compareDoubles = function compareDoubles (a, b) {
103302           return a > b ? 1 : a < b ? -1 : 0
103303         };
103304         staticAccessors$23.IntersectsOp.get = function () { return IntersectsOp };
103305         staticAccessors$23.serialVersionUID.get = function () { return -3886435814360241337 };
103306         staticAccessors$23.DEFAULT_NODE_CAPACITY.get = function () { return 10 };
103307
103308         Object.defineProperties( AbstractSTRtree, staticAccessors$23 );
103309
103310         var IntersectsOp = function IntersectsOp () {};
103311
103312         var ItemDistance = function ItemDistance () {};
103313
103314         ItemDistance.prototype.distance = function distance (item1, item2) {};
103315         ItemDistance.prototype.interfaces_ = function interfaces_ () {
103316           return []
103317         };
103318         ItemDistance.prototype.getClass = function getClass () {
103319           return ItemDistance
103320         };
103321
103322         var STRtree = (function (AbstractSTRtree$$1) {
103323           function STRtree (nodeCapacity) {
103324             nodeCapacity = nodeCapacity || STRtree.DEFAULT_NODE_CAPACITY;
103325             AbstractSTRtree$$1.call(this, nodeCapacity);
103326           }
103327
103328           if ( AbstractSTRtree$$1 ) STRtree.__proto__ = AbstractSTRtree$$1;
103329           STRtree.prototype = Object.create( AbstractSTRtree$$1 && AbstractSTRtree$$1.prototype );
103330           STRtree.prototype.constructor = STRtree;
103331
103332           var staticAccessors = { STRtreeNode: { configurable: true },serialVersionUID: { configurable: true },xComparator: { configurable: true },yComparator: { configurable: true },intersectsOp: { configurable: true },DEFAULT_NODE_CAPACITY: { configurable: true } };
103333           STRtree.prototype.createParentBoundablesFromVerticalSlices = function createParentBoundablesFromVerticalSlices (verticalSlices, newLevel) {
103334             var this$1 = this;
103335
103336             Assert.isTrue(verticalSlices.length > 0);
103337             var parentBoundables = new ArrayList();
103338             for (var i = 0; i < verticalSlices.length; i++) {
103339               parentBoundables.addAll(this$1.createParentBoundablesFromVerticalSlice(verticalSlices[i], newLevel));
103340             }
103341             return parentBoundables
103342           };
103343           STRtree.prototype.createNode = function createNode (level) {
103344             return new STRtreeNode(level)
103345           };
103346           STRtree.prototype.size = function size () {
103347             if (arguments.length === 0) {
103348               return AbstractSTRtree$$1.prototype.size.call(this)
103349             } else { return AbstractSTRtree$$1.prototype.size.apply(this, arguments) }
103350           };
103351           STRtree.prototype.insert = function insert () {
103352             if (arguments.length === 2) {
103353               var itemEnv = arguments[0];
103354               var item = arguments[1];
103355               if (itemEnv.isNull()) {
103356                 return null
103357               }
103358               AbstractSTRtree$$1.prototype.insert.call(this, itemEnv, item);
103359             } else { return AbstractSTRtree$$1.prototype.insert.apply(this, arguments) }
103360           };
103361           STRtree.prototype.getIntersectsOp = function getIntersectsOp () {
103362             return STRtree.intersectsOp
103363           };
103364           STRtree.prototype.verticalSlices = function verticalSlices (childBoundables, sliceCount) {
103365             var sliceCapacity = Math.trunc(Math.ceil(childBoundables.size() / sliceCount));
103366             var slices = new Array(sliceCount).fill(null);
103367             var i = childBoundables.iterator();
103368             for (var j = 0; j < sliceCount; j++) {
103369               slices[j] = new ArrayList();
103370               var boundablesAddedToSlice = 0;
103371               while (i.hasNext() && boundablesAddedToSlice < sliceCapacity) {
103372                 var childBoundable = i.next();
103373                 slices[j].add(childBoundable);
103374                 boundablesAddedToSlice++;
103375               }
103376             }
103377             return slices
103378           };
103379           STRtree.prototype.query = function query () {
103380             if (arguments.length === 1) {
103381               var searchEnv = arguments[0];
103382               return AbstractSTRtree$$1.prototype.query.call(this, searchEnv)
103383             } else if (arguments.length === 2) {
103384               var searchEnv$1 = arguments[0];
103385               var visitor = arguments[1];
103386               AbstractSTRtree$$1.prototype.query.call(this, searchEnv$1, visitor);
103387             } else if (arguments.length === 3) {
103388               if (hasInterface(arguments[2], ItemVisitor) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103389                 var searchBounds = arguments[0];
103390                 var node = arguments[1];
103391                 var visitor$1 = arguments[2];
103392                 AbstractSTRtree$$1.prototype.query.call(this, searchBounds, node, visitor$1);
103393               } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103394                 var searchBounds$1 = arguments[0];
103395                 var node$1 = arguments[1];
103396                 var matches = arguments[2];
103397                 AbstractSTRtree$$1.prototype.query.call(this, searchBounds$1, node$1, matches);
103398               }
103399             }
103400           };
103401           STRtree.prototype.getComparator = function getComparator () {
103402             return STRtree.yComparator
103403           };
103404           STRtree.prototype.createParentBoundablesFromVerticalSlice = function createParentBoundablesFromVerticalSlice (childBoundables, newLevel) {
103405             return AbstractSTRtree$$1.prototype.createParentBoundables.call(this, childBoundables, newLevel)
103406           };
103407           STRtree.prototype.remove = function remove () {
103408             if (arguments.length === 2) {
103409               var itemEnv = arguments[0];
103410               var item = arguments[1];
103411               return AbstractSTRtree$$1.prototype.remove.call(this, itemEnv, item)
103412             } else { return AbstractSTRtree$$1.prototype.remove.apply(this, arguments) }
103413           };
103414           STRtree.prototype.depth = function depth () {
103415             if (arguments.length === 0) {
103416               return AbstractSTRtree$$1.prototype.depth.call(this)
103417             } else { return AbstractSTRtree$$1.prototype.depth.apply(this, arguments) }
103418           };
103419           STRtree.prototype.createParentBoundables = function createParentBoundables (childBoundables, newLevel) {
103420             Assert.isTrue(!childBoundables.isEmpty());
103421             var minLeafCount = Math.trunc(Math.ceil(childBoundables.size() / this.getNodeCapacity()));
103422             var sortedChildBoundables = new ArrayList(childBoundables);
103423             Collections.sort(sortedChildBoundables, STRtree.xComparator);
103424             var verticalSlices = this.verticalSlices(sortedChildBoundables, Math.trunc(Math.ceil(Math.sqrt(minLeafCount))));
103425             return this.createParentBoundablesFromVerticalSlices(verticalSlices, newLevel)
103426           };
103427           STRtree.prototype.nearestNeighbour = function nearestNeighbour () {
103428             if (arguments.length === 1) {
103429               if (hasInterface(arguments[0], ItemDistance)) {
103430                 var itemDist = arguments[0];
103431                 var bp = new BoundablePair(this.getRoot(), this.getRoot(), itemDist);
103432                 return this.nearestNeighbour(bp)
103433               } else if (arguments[0] instanceof BoundablePair) {
103434                 var initBndPair = arguments[0];
103435                 return this.nearestNeighbour(initBndPair, Double.POSITIVE_INFINITY)
103436               }
103437             } else if (arguments.length === 2) {
103438               if (arguments[0] instanceof STRtree && hasInterface(arguments[1], ItemDistance)) {
103439                 var tree = arguments[0];
103440                 var itemDist$1 = arguments[1];
103441                 var bp$1 = new BoundablePair(this.getRoot(), tree.getRoot(), itemDist$1);
103442                 return this.nearestNeighbour(bp$1)
103443               } else if (arguments[0] instanceof BoundablePair && typeof arguments[1] === 'number') {
103444                 var initBndPair$1 = arguments[0];
103445                 var maxDistance = arguments[1];
103446                 var distanceLowerBound = maxDistance;
103447                 var minPair = null;
103448                 var priQ = new PriorityQueue();
103449                 priQ.add(initBndPair$1);
103450                 while (!priQ.isEmpty() && distanceLowerBound > 0.0) {
103451                   var bndPair = priQ.poll();
103452                   var currentDistance = bndPair.getDistance();
103453                   if (currentDistance >= distanceLowerBound) { break }
103454                   if (bndPair.isLeaves()) {
103455                     distanceLowerBound = currentDistance;
103456                     minPair = bndPair;
103457                   } else {
103458                     bndPair.expandToQueue(priQ, distanceLowerBound);
103459                   }
103460                 }
103461                 return [minPair.getBoundable(0).getItem(), minPair.getBoundable(1).getItem()]
103462               }
103463             } else if (arguments.length === 3) {
103464               var env = arguments[0];
103465               var item = arguments[1];
103466               var itemDist$2 = arguments[2];
103467               var bnd = new ItemBoundable(env, item);
103468               var bp$2 = new BoundablePair(this.getRoot(), bnd, itemDist$2);
103469               return this.nearestNeighbour(bp$2)[0]
103470             }
103471           };
103472           STRtree.prototype.interfaces_ = function interfaces_ () {
103473             return [SpatialIndex, Serializable]
103474           };
103475           STRtree.prototype.getClass = function getClass () {
103476             return STRtree
103477           };
103478           STRtree.centreX = function centreX (e) {
103479             return STRtree.avg(e.getMinX(), e.getMaxX())
103480           };
103481           STRtree.avg = function avg (a, b) {
103482             return (a + b) / 2
103483           };
103484           STRtree.centreY = function centreY (e) {
103485             return STRtree.avg(e.getMinY(), e.getMaxY())
103486           };
103487           staticAccessors.STRtreeNode.get = function () { return STRtreeNode };
103488           staticAccessors.serialVersionUID.get = function () { return 259274702368956900 };
103489           staticAccessors.xComparator.get = function () {
103490             return {
103491               interfaces_: function () {
103492                 return [Comparator]
103493               },
103494               compare: function (o1, o2) {
103495                 return AbstractSTRtree$$1.compareDoubles(STRtree.centreX(o1.getBounds()), STRtree.centreX(o2.getBounds()))
103496               }
103497             }
103498           };
103499           staticAccessors.yComparator.get = function () {
103500             return {
103501               interfaces_: function () {
103502                 return [Comparator]
103503               },
103504               compare: function (o1, o2) {
103505                 return AbstractSTRtree$$1.compareDoubles(STRtree.centreY(o1.getBounds()), STRtree.centreY(o2.getBounds()))
103506               }
103507             }
103508           };
103509           staticAccessors.intersectsOp.get = function () {
103510             return {
103511               interfaces_: function () {
103512                 return [AbstractSTRtree$$1.IntersectsOp]
103513               },
103514               intersects: function (aBounds, bBounds) {
103515                 return aBounds.intersects(bBounds)
103516               }
103517             }
103518           };
103519           staticAccessors.DEFAULT_NODE_CAPACITY.get = function () { return 10 };
103520
103521           Object.defineProperties( STRtree, staticAccessors );
103522
103523           return STRtree;
103524         }(AbstractSTRtree));
103525
103526         var STRtreeNode = (function (AbstractNode$$1) {
103527           function STRtreeNode () {
103528             var level = arguments[0];
103529             AbstractNode$$1.call(this, level);
103530           }
103531
103532           if ( AbstractNode$$1 ) STRtreeNode.__proto__ = AbstractNode$$1;
103533           STRtreeNode.prototype = Object.create( AbstractNode$$1 && AbstractNode$$1.prototype );
103534           STRtreeNode.prototype.constructor = STRtreeNode;
103535           STRtreeNode.prototype.computeBounds = function computeBounds () {
103536             var bounds = null;
103537             for (var i = this.getChildBoundables().iterator(); i.hasNext();) {
103538               var childBoundable = i.next();
103539               if (bounds === null) {
103540                 bounds = new Envelope(childBoundable.getBounds());
103541               } else {
103542                 bounds.expandToInclude(childBoundable.getBounds());
103543               }
103544             }
103545             return bounds
103546           };
103547           STRtreeNode.prototype.interfaces_ = function interfaces_ () {
103548             return []
103549           };
103550           STRtreeNode.prototype.getClass = function getClass () {
103551             return STRtreeNode
103552           };
103553
103554           return STRtreeNode;
103555         }(AbstractNode));
103556
103557         var SegmentPointComparator = function SegmentPointComparator () {};
103558
103559         SegmentPointComparator.prototype.interfaces_ = function interfaces_ () {
103560           return []
103561         };
103562         SegmentPointComparator.prototype.getClass = function getClass () {
103563           return SegmentPointComparator
103564         };
103565         SegmentPointComparator.relativeSign = function relativeSign (x0, x1) {
103566           if (x0 < x1) { return -1 }
103567           if (x0 > x1) { return 1 }
103568           return 0
103569         };
103570         SegmentPointComparator.compare = function compare (octant, p0, p1) {
103571           if (p0.equals2D(p1)) { return 0 }
103572           var xSign = SegmentPointComparator.relativeSign(p0.x, p1.x);
103573           var ySign = SegmentPointComparator.relativeSign(p0.y, p1.y);
103574           switch (octant) {
103575             case 0:
103576               return SegmentPointComparator.compareValue(xSign, ySign)
103577             case 1:
103578               return SegmentPointComparator.compareValue(ySign, xSign)
103579             case 2:
103580               return SegmentPointComparator.compareValue(ySign, -xSign)
103581             case 3:
103582               return SegmentPointComparator.compareValue(-xSign, ySign)
103583             case 4:
103584               return SegmentPointComparator.compareValue(-xSign, -ySign)
103585             case 5:
103586               return SegmentPointComparator.compareValue(-ySign, -xSign)
103587             case 6:
103588               return SegmentPointComparator.compareValue(-ySign, xSign)
103589             case 7:
103590               return SegmentPointComparator.compareValue(xSign, -ySign)
103591           }
103592           Assert.shouldNeverReachHere('invalid octant value');
103593           return 0
103594         };
103595         SegmentPointComparator.compareValue = function compareValue (compareSign0, compareSign1) {
103596           if (compareSign0 < 0) { return -1 }
103597           if (compareSign0 > 0) { return 1 }
103598           if (compareSign1 < 0) { return -1 }
103599           if (compareSign1 > 0) { return 1 }
103600           return 0
103601         };
103602
103603         var SegmentNode = function SegmentNode () {
103604           this._segString = null;
103605           this.coord = null;
103606           this.segmentIndex = null;
103607           this._segmentOctant = null;
103608           this._isInterior = null;
103609           var segString = arguments[0];
103610           var coord = arguments[1];
103611           var segmentIndex = arguments[2];
103612           var segmentOctant = arguments[3];
103613           this._segString = segString;
103614           this.coord = new Coordinate(coord);
103615           this.segmentIndex = segmentIndex;
103616           this._segmentOctant = segmentOctant;
103617           this._isInterior = !coord.equals2D(segString.getCoordinate(segmentIndex));
103618         };
103619         SegmentNode.prototype.getCoordinate = function getCoordinate () {
103620           return this.coord
103621         };
103622         SegmentNode.prototype.print = function print (out) {
103623           out.print(this.coord);
103624           out.print(' seg # = ' + this.segmentIndex);
103625         };
103626         SegmentNode.prototype.compareTo = function compareTo (obj) {
103627           var other = obj;
103628           if (this.segmentIndex < other.segmentIndex) { return -1 }
103629           if (this.segmentIndex > other.segmentIndex) { return 1 }
103630           if (this.coord.equals2D(other.coord)) { return 0 }
103631           return SegmentPointComparator.compare(this._segmentOctant, this.coord, other.coord)
103632         };
103633         SegmentNode.prototype.isEndPoint = function isEndPoint (maxSegmentIndex) {
103634           if (this.segmentIndex === 0 && !this._isInterior) { return true }
103635           if (this.segmentIndex === maxSegmentIndex) { return true }
103636           return false
103637         };
103638         SegmentNode.prototype.isInterior = function isInterior () {
103639           return this._isInterior
103640         };
103641         SegmentNode.prototype.interfaces_ = function interfaces_ () {
103642           return [Comparable]
103643         };
103644         SegmentNode.prototype.getClass = function getClass () {
103645           return SegmentNode
103646         };
103647
103648         // import Iterator from '../../../../java/util/Iterator'
103649         var SegmentNodeList = function SegmentNodeList () {
103650           this._nodeMap = new TreeMap();
103651           this._edge = null;
103652           var edge = arguments[0];
103653           this._edge = edge;
103654         };
103655         SegmentNodeList.prototype.getSplitCoordinates = function getSplitCoordinates () {
103656             var this$1 = this;
103657
103658           var coordList = new CoordinateList();
103659           this.addEndpoints();
103660           var it = this.iterator();
103661           var eiPrev = it.next();
103662           while (it.hasNext()) {
103663             var ei = it.next();
103664             this$1.addEdgeCoordinates(eiPrev, ei, coordList);
103665             eiPrev = ei;
103666           }
103667           return coordList.toCoordinateArray()
103668         };
103669         SegmentNodeList.prototype.addCollapsedNodes = function addCollapsedNodes () {
103670             var this$1 = this;
103671
103672           var collapsedVertexIndexes = new ArrayList();
103673           this.findCollapsesFromInsertedNodes(collapsedVertexIndexes);
103674           this.findCollapsesFromExistingVertices(collapsedVertexIndexes);
103675           for (var it = collapsedVertexIndexes.iterator(); it.hasNext();) {
103676             var vertexIndex = it.next().intValue();
103677             this$1.add(this$1._edge.getCoordinate(vertexIndex), vertexIndex);
103678           }
103679         };
103680         SegmentNodeList.prototype.print = function print (out) {
103681           out.println('Intersections:');
103682           for (var it = this.iterator(); it.hasNext();) {
103683             var ei = it.next();
103684             ei.print(out);
103685           }
103686         };
103687         SegmentNodeList.prototype.findCollapsesFromExistingVertices = function findCollapsesFromExistingVertices (collapsedVertexIndexes) {
103688             var this$1 = this;
103689
103690           for (var i = 0; i < this._edge.size() - 2; i++) {
103691             var p0 = this$1._edge.getCoordinate(i);
103692             // const p1 = this._edge.getCoordinate(i + 1)
103693             var p2 = this$1._edge.getCoordinate(i + 2);
103694             if (p0.equals2D(p2)) {
103695               collapsedVertexIndexes.add(new Integer(i + 1));
103696             }
103697           }
103698         };
103699         SegmentNodeList.prototype.addEdgeCoordinates = function addEdgeCoordinates (ei0, ei1, coordList) {
103700             var this$1 = this;
103701
103702           // let npts = ei1.segmentIndex - ei0.segmentIndex + 2
103703           var lastSegStartPt = this._edge.getCoordinate(ei1.segmentIndex);
103704           var useIntPt1 = ei1.isInterior() || !ei1.coord.equals2D(lastSegStartPt);
103705           // if (!useIntPt1) {
103706           // npts--
103707           // }
103708           // const ipt = 0
103709           coordList.add(new Coordinate(ei0.coord), false);
103710           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
103711             coordList.add(this$1._edge.getCoordinate(i));
103712           }
103713           if (useIntPt1) {
103714             coordList.add(new Coordinate(ei1.coord));
103715           }
103716         };
103717         SegmentNodeList.prototype.iterator = function iterator () {
103718           return this._nodeMap.values().iterator()
103719         };
103720         SegmentNodeList.prototype.addSplitEdges = function addSplitEdges (edgeList) {
103721             var this$1 = this;
103722
103723           this.addEndpoints();
103724           this.addCollapsedNodes();
103725           var it = this.iterator();
103726           var eiPrev = it.next();
103727           while (it.hasNext()) {
103728             var ei = it.next();
103729             var newEdge = this$1.createSplitEdge(eiPrev, ei);
103730             edgeList.add(newEdge);
103731             eiPrev = ei;
103732           }
103733         };
103734         SegmentNodeList.prototype.findCollapseIndex = function findCollapseIndex (ei0, ei1, collapsedVertexIndex) {
103735           if (!ei0.coord.equals2D(ei1.coord)) { return false }
103736           var numVerticesBetween = ei1.segmentIndex - ei0.segmentIndex;
103737           if (!ei1.isInterior()) {
103738             numVerticesBetween--;
103739           }
103740           if (numVerticesBetween === 1) {
103741             collapsedVertexIndex[0] = ei0.segmentIndex + 1;
103742             return true
103743           }
103744           return false
103745         };
103746         SegmentNodeList.prototype.findCollapsesFromInsertedNodes = function findCollapsesFromInsertedNodes (collapsedVertexIndexes) {
103747             var this$1 = this;
103748
103749           var collapsedVertexIndex = new Array(1).fill(null);
103750           var it = this.iterator();
103751           var eiPrev = it.next();
103752           while (it.hasNext()) {
103753             var ei = it.next();
103754             var isCollapsed = this$1.findCollapseIndex(eiPrev, ei, collapsedVertexIndex);
103755             if (isCollapsed) { collapsedVertexIndexes.add(new Integer(collapsedVertexIndex[0])); }
103756             eiPrev = ei;
103757           }
103758         };
103759         SegmentNodeList.prototype.getEdge = function getEdge () {
103760           return this._edge
103761         };
103762         SegmentNodeList.prototype.addEndpoints = function addEndpoints () {
103763           var maxSegIndex = this._edge.size() - 1;
103764           this.add(this._edge.getCoordinate(0), 0);
103765           this.add(this._edge.getCoordinate(maxSegIndex), maxSegIndex);
103766         };
103767         SegmentNodeList.prototype.createSplitEdge = function createSplitEdge (ei0, ei1) {
103768             var this$1 = this;
103769
103770           var npts = ei1.segmentIndex - ei0.segmentIndex + 2;
103771           var lastSegStartPt = this._edge.getCoordinate(ei1.segmentIndex);
103772           var useIntPt1 = ei1.isInterior() || !ei1.coord.equals2D(lastSegStartPt);
103773           if (!useIntPt1) {
103774             npts--;
103775           }
103776           var pts = new Array(npts).fill(null);
103777           var ipt = 0;
103778           pts[ipt++] = new Coordinate(ei0.coord);
103779           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
103780             pts[ipt++] = this$1._edge.getCoordinate(i);
103781           }
103782           if (useIntPt1) { pts[ipt] = new Coordinate(ei1.coord); }
103783           return new NodedSegmentString(pts, this._edge.getData())
103784         };
103785         SegmentNodeList.prototype.add = function add (intPt, segmentIndex) {
103786           var eiNew = new SegmentNode(this._edge, intPt, segmentIndex, this._edge.getSegmentOctant(segmentIndex));
103787           var ei = this._nodeMap.get(eiNew);
103788           if (ei !== null) {
103789             Assert.isTrue(ei.coord.equals2D(intPt), 'Found equal nodes with different coordinates');
103790             return ei
103791           }
103792           this._nodeMap.put(eiNew, eiNew);
103793           return eiNew
103794         };
103795         SegmentNodeList.prototype.checkSplitEdgesCorrectness = function checkSplitEdgesCorrectness (splitEdges) {
103796           var edgePts = this._edge.getCoordinates();
103797           var split0 = splitEdges.get(0);
103798           var pt0 = split0.getCoordinate(0);
103799           if (!pt0.equals2D(edgePts[0])) { throw new RuntimeException('bad split edge start point at ' + pt0) }
103800           var splitn = splitEdges.get(splitEdges.size() - 1);
103801           var splitnPts = splitn.getCoordinates();
103802           var ptn = splitnPts[splitnPts.length - 1];
103803           if (!ptn.equals2D(edgePts[edgePts.length - 1])) { throw new RuntimeException('bad split edge end point at ' + ptn) }
103804         };
103805         SegmentNodeList.prototype.interfaces_ = function interfaces_ () {
103806           return []
103807         };
103808         SegmentNodeList.prototype.getClass = function getClass () {
103809           return SegmentNodeList
103810         };
103811
103812
103813
103814         // class NodeVertexIterator {
103815         //   constructor () {
103816         //     this._nodeList = null
103817         //     this._edge = null
103818         //     this._nodeIt = null
103819         //     this._currNode = null
103820         //     this._nextNode = null
103821         //     this._currSegIndex = 0
103822         //     let nodeList = arguments[0]
103823         //     this._nodeList = nodeList
103824         //     this._edge = nodeList.getEdge()
103825         //     this._nodeIt = nodeList.iterator()
103826         //     this.readNextNode()
103827         //   }
103828         //   next () {
103829         //     if (this._currNode === null) {
103830         //       this._currNode = this._nextNode
103831         //       this._currSegIndex = this._currNode.segmentIndex
103832         //       this.readNextNode()
103833         //       return this._currNode
103834         //     }
103835         //     if (this._nextNode === null) return null
103836         //     if (this._nextNode.segmentIndex === this._currNode.segmentIndex) {
103837         //       this._currNode = this._nextNode
103838         //       this._currSegIndex = this._currNode.segmentIndex
103839         //       this.readNextNode()
103840         //       return this._currNode
103841         //     }
103842         //     if (this._nextNode.segmentIndex > this._currNode.segmentIndex) {}
103843         //     return null
103844         //   }
103845         //   remove () {
103846         //     // throw new UnsupportedOperationException(this.getClass().getName())
103847         //   }
103848         //   hasNext () {
103849         //     if (this._nextNode === null) return false
103850         //     return true
103851         //   }
103852         //   readNextNode () {
103853         //     if (this._nodeIt.hasNext()) this._nextNode = this._nodeIt.next(); else this._nextNode = null
103854         //   }
103855         //   interfaces_ () {
103856         //     return [Iterator]
103857         //   }
103858         //   getClass () {
103859         //     return NodeVertexIterator
103860         //   }
103861         // }
103862
103863         var Octant = function Octant () {};
103864
103865         Octant.prototype.interfaces_ = function interfaces_ () {
103866           return []
103867         };
103868         Octant.prototype.getClass = function getClass () {
103869           return Octant
103870         };
103871         Octant.octant = function octant () {
103872           if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
103873             var dx = arguments[0];
103874             var dy = arguments[1];
103875             if (dx === 0.0 && dy === 0.0) { throw new IllegalArgumentException('Cannot compute the octant for point ( ' + dx + ', ' + dy + ' )') }
103876             var adx = Math.abs(dx);
103877             var ady = Math.abs(dy);
103878             if (dx >= 0) {
103879               if (dy >= 0) {
103880                 if (adx >= ady) { return 0; } else { return 1 }
103881               } else {
103882                 if (adx >= ady) { return 7; } else { return 6 }
103883               }
103884             } else {
103885               if (dy >= 0) {
103886                 if (adx >= ady) { return 3; } else { return 2 }
103887               } else {
103888                 if (adx >= ady) { return 4; } else { return 5 }
103889               }
103890             }
103891           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
103892             var p0 = arguments[0];
103893             var p1 = arguments[1];
103894             var dx$1 = p1.x - p0.x;
103895             var dy$1 = p1.y - p0.y;
103896             if (dx$1 === 0.0 && dy$1 === 0.0) { throw new IllegalArgumentException('Cannot compute the octant for two identical points ' + p0) }
103897             return Octant.octant(dx$1, dy$1)
103898           }
103899         };
103900
103901         var SegmentString = function SegmentString () {};
103902
103903         SegmentString.prototype.getCoordinates = function getCoordinates () {};
103904         SegmentString.prototype.size = function size () {};
103905         SegmentString.prototype.getCoordinate = function getCoordinate (i) {};
103906         SegmentString.prototype.isClosed = function isClosed () {};
103907         SegmentString.prototype.setData = function setData (data) {};
103908         SegmentString.prototype.getData = function getData () {};
103909         SegmentString.prototype.interfaces_ = function interfaces_ () {
103910           return []
103911         };
103912         SegmentString.prototype.getClass = function getClass () {
103913           return SegmentString
103914         };
103915
103916         var NodableSegmentString = function NodableSegmentString () {};
103917
103918         NodableSegmentString.prototype.addIntersection = function addIntersection (intPt, segmentIndex) {};
103919         NodableSegmentString.prototype.interfaces_ = function interfaces_ () {
103920           return [SegmentString]
103921         };
103922         NodableSegmentString.prototype.getClass = function getClass () {
103923           return NodableSegmentString
103924         };
103925
103926         var NodedSegmentString = function NodedSegmentString () {
103927           this._nodeList = new SegmentNodeList(this);
103928           this._pts = null;
103929           this._data = null;
103930           var pts = arguments[0];
103931           var data = arguments[1];
103932           this._pts = pts;
103933           this._data = data;
103934         };
103935         NodedSegmentString.prototype.getCoordinates = function getCoordinates () {
103936           return this._pts
103937         };
103938         NodedSegmentString.prototype.size = function size () {
103939           return this._pts.length
103940         };
103941         NodedSegmentString.prototype.getCoordinate = function getCoordinate (i) {
103942           return this._pts[i]
103943         };
103944         NodedSegmentString.prototype.isClosed = function isClosed () {
103945           return this._pts[0].equals(this._pts[this._pts.length - 1])
103946         };
103947         NodedSegmentString.prototype.getSegmentOctant = function getSegmentOctant (index) {
103948           if (index === this._pts.length - 1) { return -1 }
103949           return this.safeOctant(this.getCoordinate(index), this.getCoordinate(index + 1))
103950         };
103951         NodedSegmentString.prototype.setData = function setData (data) {
103952           this._data = data;
103953         };
103954         NodedSegmentString.prototype.safeOctant = function safeOctant (p0, p1) {
103955           if (p0.equals2D(p1)) { return 0 }
103956           return Octant.octant(p0, p1)
103957         };
103958         NodedSegmentString.prototype.getData = function getData () {
103959           return this._data
103960         };
103961         NodedSegmentString.prototype.addIntersection = function addIntersection () {
103962           if (arguments.length === 2) {
103963             var intPt$1 = arguments[0];
103964             var segmentIndex = arguments[1];
103965             this.addIntersectionNode(intPt$1, segmentIndex);
103966           } else if (arguments.length === 4) {
103967             var li = arguments[0];
103968             var segmentIndex$1 = arguments[1];
103969             // const geomIndex = arguments[2]
103970             var intIndex = arguments[3];
103971             var intPt = new Coordinate(li.getIntersection(intIndex));
103972             this.addIntersection(intPt, segmentIndex$1);
103973           }
103974         };
103975         NodedSegmentString.prototype.toString = function toString () {
103976           return WKTWriter.toLineString(new CoordinateArraySequence(this._pts))
103977         };
103978         NodedSegmentString.prototype.getNodeList = function getNodeList () {
103979           return this._nodeList
103980         };
103981         NodedSegmentString.prototype.addIntersectionNode = function addIntersectionNode (intPt, segmentIndex) {
103982           var normalizedSegmentIndex = segmentIndex;
103983           var nextSegIndex = normalizedSegmentIndex + 1;
103984           if (nextSegIndex < this._pts.length) {
103985             var nextPt = this._pts[nextSegIndex];
103986             if (intPt.equals2D(nextPt)) {
103987               normalizedSegmentIndex = nextSegIndex;
103988             }
103989           }
103990           var ei = this._nodeList.add(intPt, normalizedSegmentIndex);
103991           return ei
103992         };
103993         NodedSegmentString.prototype.addIntersections = function addIntersections (li, segmentIndex, geomIndex) {
103994             var this$1 = this;
103995
103996           for (var i = 0; i < li.getIntersectionNum(); i++) {
103997             this$1.addIntersection(li, segmentIndex, geomIndex, i);
103998           }
103999         };
104000         NodedSegmentString.prototype.interfaces_ = function interfaces_ () {
104001           return [NodableSegmentString]
104002         };
104003         NodedSegmentString.prototype.getClass = function getClass () {
104004           return NodedSegmentString
104005         };
104006         NodedSegmentString.getNodedSubstrings = function getNodedSubstrings () {
104007           if (arguments.length === 1) {
104008             var segStrings = arguments[0];
104009             var resultEdgelist = new ArrayList();
104010             NodedSegmentString.getNodedSubstrings(segStrings, resultEdgelist);
104011             return resultEdgelist
104012           } else if (arguments.length === 2) {
104013             var segStrings$1 = arguments[0];
104014             var resultEdgelist$1 = arguments[1];
104015             for (var i = segStrings$1.iterator(); i.hasNext();) {
104016               var ss = i.next();
104017               ss.getNodeList().addSplitEdges(resultEdgelist$1);
104018             }
104019           }
104020         };
104021
104022         var LineSegment = function LineSegment () {
104023           this.p0 = null;
104024           this.p1 = null;
104025           if (arguments.length === 0) {
104026             this.p0 = new Coordinate();
104027             this.p1 = new Coordinate();
104028           } else if (arguments.length === 1) {
104029             var ls = arguments[0];
104030             this.p0 = new Coordinate(ls.p0);
104031             this.p1 = new Coordinate(ls.p1);
104032           } else if (arguments.length === 2) {
104033             this.p0 = arguments[0];
104034             this.p1 = arguments[1];
104035           } else if (arguments.length === 4) {
104036             var x0 = arguments[0];
104037             var y0 = arguments[1];
104038             var x1 = arguments[2];
104039             var y1 = arguments[3];
104040             this.p0 = new Coordinate(x0, y0);
104041             this.p1 = new Coordinate(x1, y1);
104042           }
104043         };
104044
104045         var staticAccessors$24 = { serialVersionUID: { configurable: true } };
104046         LineSegment.prototype.minX = function minX () {
104047           return Math.min(this.p0.x, this.p1.x)
104048         };
104049         LineSegment.prototype.orientationIndex = function orientationIndex () {
104050           if (arguments[0] instanceof LineSegment) {
104051             var seg = arguments[0];
104052             var orient0 = CGAlgorithms.orientationIndex(this.p0, this.p1, seg.p0);
104053             var orient1 = CGAlgorithms.orientationIndex(this.p0, this.p1, seg.p1);
104054             if (orient0 >= 0 && orient1 >= 0) { return Math.max(orient0, orient1) }
104055             if (orient0 <= 0 && orient1 <= 0) { return Math.max(orient0, orient1) }
104056             return 0
104057           } else if (arguments[0] instanceof Coordinate) {
104058             var p = arguments[0];
104059             return CGAlgorithms.orientationIndex(this.p0, this.p1, p)
104060           }
104061         };
104062         LineSegment.prototype.toGeometry = function toGeometry (geomFactory) {
104063           return geomFactory.createLineString([this.p0, this.p1])
104064         };
104065         LineSegment.prototype.isVertical = function isVertical () {
104066           return this.p0.x === this.p1.x
104067         };
104068         LineSegment.prototype.equals = function equals (o) {
104069           if (!(o instanceof LineSegment)) {
104070             return false
104071           }
104072           var other = o;
104073           return this.p0.equals(other.p0) && this.p1.equals(other.p1)
104074         };
104075         LineSegment.prototype.intersection = function intersection (line) {
104076           var li = new RobustLineIntersector();
104077           li.computeIntersection(this.p0, this.p1, line.p0, line.p1);
104078           if (li.hasIntersection()) { return li.getIntersection(0) }
104079           return null
104080         };
104081         LineSegment.prototype.project = function project () {
104082           if (arguments[0] instanceof Coordinate) {
104083             var p = arguments[0];
104084             if (p.equals(this.p0) || p.equals(this.p1)) { return new Coordinate(p) }
104085             var r = this.projectionFactor(p);
104086             var coord = new Coordinate();
104087             coord.x = this.p0.x + r * (this.p1.x - this.p0.x);
104088             coord.y = this.p0.y + r * (this.p1.y - this.p0.y);
104089             return coord
104090           } else if (arguments[0] instanceof LineSegment) {
104091             var seg = arguments[0];
104092             var pf0 = this.projectionFactor(seg.p0);
104093             var pf1 = this.projectionFactor(seg.p1);
104094             if (pf0 >= 1.0 && pf1 >= 1.0) { return null }
104095             if (pf0 <= 0.0 && pf1 <= 0.0) { return null }
104096             var newp0 = this.project(seg.p0);
104097             if (pf0 < 0.0) { newp0 = this.p0; }
104098             if (pf0 > 1.0) { newp0 = this.p1; }
104099             var newp1 = this.project(seg.p1);
104100             if (pf1 < 0.0) { newp1 = this.p0; }
104101             if (pf1 > 1.0) { newp1 = this.p1; }
104102             return new LineSegment(newp0, newp1)
104103           }
104104         };
104105         LineSegment.prototype.normalize = function normalize () {
104106           if (this.p1.compareTo(this.p0) < 0) { this.reverse(); }
104107         };
104108         LineSegment.prototype.angle = function angle () {
104109           return Math.atan2(this.p1.y - this.p0.y, this.p1.x - this.p0.x)
104110         };
104111         LineSegment.prototype.getCoordinate = function getCoordinate (i) {
104112           if (i === 0) { return this.p0 }
104113           return this.p1
104114         };
104115         LineSegment.prototype.distancePerpendicular = function distancePerpendicular (p) {
104116           return CGAlgorithms.distancePointLinePerpendicular(p, this.p0, this.p1)
104117         };
104118         LineSegment.prototype.minY = function minY () {
104119           return Math.min(this.p0.y, this.p1.y)
104120         };
104121         LineSegment.prototype.midPoint = function midPoint () {
104122           return LineSegment.midPoint(this.p0, this.p1)
104123         };
104124         LineSegment.prototype.projectionFactor = function projectionFactor (p) {
104125           if (p.equals(this.p0)) { return 0.0 }
104126           if (p.equals(this.p1)) { return 1.0 }
104127           var dx = this.p1.x - this.p0.x;
104128           var dy = this.p1.y - this.p0.y;
104129           var len = dx * dx + dy * dy;
104130           if (len <= 0.0) { return Double.NaN }
104131           var r = ((p.x - this.p0.x) * dx + (p.y - this.p0.y) * dy) / len;
104132           return r
104133         };
104134         LineSegment.prototype.closestPoints = function closestPoints (line) {
104135           var intPt = this.intersection(line);
104136           if (intPt !== null) {
104137             return [intPt, intPt]
104138           }
104139           var closestPt = new Array(2).fill(null);
104140           var minDistance = Double.MAX_VALUE;
104141           var dist = null;
104142           var close00 = this.closestPoint(line.p0);
104143           minDistance = close00.distance(line.p0);
104144           closestPt[0] = close00;
104145           closestPt[1] = line.p0;
104146           var close01 = this.closestPoint(line.p1);
104147           dist = close01.distance(line.p1);
104148           if (dist < minDistance) {
104149             minDistance = dist;
104150             closestPt[0] = close01;
104151             closestPt[1] = line.p1;
104152           }
104153           var close10 = line.closestPoint(this.p0);
104154           dist = close10.distance(this.p0);
104155           if (dist < minDistance) {
104156             minDistance = dist;
104157             closestPt[0] = this.p0;
104158             closestPt[1] = close10;
104159           }
104160           var close11 = line.closestPoint(this.p1);
104161           dist = close11.distance(this.p1);
104162           if (dist < minDistance) {
104163             minDistance = dist;
104164             closestPt[0] = this.p1;
104165             closestPt[1] = close11;
104166           }
104167           return closestPt
104168         };
104169         LineSegment.prototype.closestPoint = function closestPoint (p) {
104170           var factor = this.projectionFactor(p);
104171           if (factor > 0 && factor < 1) {
104172             return this.project(p)
104173           }
104174           var dist0 = this.p0.distance(p);
104175           var dist1 = this.p1.distance(p);
104176           if (dist0 < dist1) { return this.p0 }
104177           return this.p1
104178         };
104179         LineSegment.prototype.maxX = function maxX () {
104180           return Math.max(this.p0.x, this.p1.x)
104181         };
104182         LineSegment.prototype.getLength = function getLength () {
104183           return this.p0.distance(this.p1)
104184         };
104185         LineSegment.prototype.compareTo = function compareTo (o) {
104186           var other = o;
104187           var comp0 = this.p0.compareTo(other.p0);
104188           if (comp0 !== 0) { return comp0 }
104189           return this.p1.compareTo(other.p1)
104190         };
104191         LineSegment.prototype.reverse = function reverse () {
104192           var temp = this.p0;
104193           this.p0 = this.p1;
104194           this.p1 = temp;
104195         };
104196         LineSegment.prototype.equalsTopo = function equalsTopo (other) {
104197           return this.p0.equals(other.p0) &&
104198                 (this.p1.equals(other.p1) || this.p0.equals(other.p1)) &&
104199                  this.p1.equals(other.p0)
104200         };
104201         LineSegment.prototype.lineIntersection = function lineIntersection (line) {
104202           try {
104203             var intPt = HCoordinate.intersection(this.p0, this.p1, line.p0, line.p1);
104204             return intPt
104205           } catch (ex) {
104206             if (ex instanceof NotRepresentableException) ; else { throw ex }
104207           } finally {}
104208           return null
104209         };
104210         LineSegment.prototype.maxY = function maxY () {
104211           return Math.max(this.p0.y, this.p1.y)
104212         };
104213         LineSegment.prototype.pointAlongOffset = function pointAlongOffset (segmentLengthFraction, offsetDistance) {
104214           var segx = this.p0.x + segmentLengthFraction * (this.p1.x - this.p0.x);
104215           var segy = this.p0.y + segmentLengthFraction * (this.p1.y - this.p0.y);
104216           var dx = this.p1.x - this.p0.x;
104217           var dy = this.p1.y - this.p0.y;
104218           var len = Math.sqrt(dx * dx + dy * dy);
104219           var ux = 0.0;
104220           var uy = 0.0;
104221           if (offsetDistance !== 0.0) {
104222             if (len <= 0.0) { throw new Error('Cannot compute offset from zero-length line segment') }
104223             ux = offsetDistance * dx / len;
104224             uy = offsetDistance * dy / len;
104225           }
104226           var offsetx = segx - uy;
104227           var offsety = segy + ux;
104228           var coord = new Coordinate(offsetx, offsety);
104229           return coord
104230         };
104231         LineSegment.prototype.setCoordinates = function setCoordinates () {
104232           if (arguments.length === 1) {
104233             var ls = arguments[0];
104234             this.setCoordinates(ls.p0, ls.p1);
104235           } else if (arguments.length === 2) {
104236             var p0 = arguments[0];
104237             var p1 = arguments[1];
104238             this.p0.x = p0.x;
104239             this.p0.y = p0.y;
104240             this.p1.x = p1.x;
104241             this.p1.y = p1.y;
104242           }
104243         };
104244         LineSegment.prototype.segmentFraction = function segmentFraction (inputPt) {
104245           var segFrac = this.projectionFactor(inputPt);
104246           if (segFrac < 0.0) { segFrac = 0.0; } else if (segFrac > 1.0 || Double.isNaN(segFrac)) { segFrac = 1.0; }
104247           return segFrac
104248         };
104249         LineSegment.prototype.toString = function toString () {
104250           return 'LINESTRING( ' + this.p0.x + ' ' + this.p0.y + ', ' + this.p1.x + ' ' + this.p1.y + ')'
104251         };
104252         LineSegment.prototype.isHorizontal = function isHorizontal () {
104253           return this.p0.y === this.p1.y
104254         };
104255         LineSegment.prototype.distance = function distance () {
104256           if (arguments[0] instanceof LineSegment) {
104257             var ls = arguments[0];
104258             return CGAlgorithms.distanceLineLine(this.p0, this.p1, ls.p0, ls.p1)
104259           } else if (arguments[0] instanceof Coordinate) {
104260             var p = arguments[0];
104261             return CGAlgorithms.distancePointLine(p, this.p0, this.p1)
104262           }
104263         };
104264         LineSegment.prototype.pointAlong = function pointAlong (segmentLengthFraction) {
104265           var coord = new Coordinate();
104266           coord.x = this.p0.x + segmentLengthFraction * (this.p1.x - this.p0.x);
104267           coord.y = this.p0.y + segmentLengthFraction * (this.p1.y - this.p0.y);
104268           return coord
104269         };
104270         LineSegment.prototype.hashCode = function hashCode () {
104271           var bits0 = Double.doubleToLongBits(this.p0.x);
104272           bits0 ^= Double.doubleToLongBits(this.p0.y) * 31;
104273           var hash0 = Math.trunc(bits0) ^ Math.trunc(bits0 >> 32);
104274           var bits1 = Double.doubleToLongBits(this.p1.x);
104275           bits1 ^= Double.doubleToLongBits(this.p1.y) * 31;
104276           var hash1 = Math.trunc(bits1) ^ Math.trunc(bits1 >> 32);
104277           return hash0 ^ hash1
104278         };
104279         LineSegment.prototype.interfaces_ = function interfaces_ () {
104280           return [Comparable, Serializable]
104281         };
104282         LineSegment.prototype.getClass = function getClass () {
104283           return LineSegment
104284         };
104285         LineSegment.midPoint = function midPoint (p0, p1) {
104286           return new Coordinate((p0.x + p1.x) / 2, (p0.y + p1.y) / 2)
104287         };
104288         staticAccessors$24.serialVersionUID.get = function () { return 3252005833466256227 };
104289
104290         Object.defineProperties( LineSegment, staticAccessors$24 );
104291
104292         var MonotoneChainOverlapAction = function MonotoneChainOverlapAction () {
104293           this.tempEnv1 = new Envelope();
104294           this.tempEnv2 = new Envelope();
104295           this._overlapSeg1 = new LineSegment();
104296           this._overlapSeg2 = new LineSegment();
104297         };
104298         MonotoneChainOverlapAction.prototype.overlap = function overlap () {
104299           if (arguments.length === 2) ; else if (arguments.length === 4) {
104300             var mc1 = arguments[0];
104301             var start1 = arguments[1];
104302             var mc2 = arguments[2];
104303             var start2 = arguments[3];
104304             mc1.getLineSegment(start1, this._overlapSeg1);
104305             mc2.getLineSegment(start2, this._overlapSeg2);
104306             this.overlap(this._overlapSeg1, this._overlapSeg2);
104307           }
104308         };
104309         MonotoneChainOverlapAction.prototype.interfaces_ = function interfaces_ () {
104310           return []
104311         };
104312         MonotoneChainOverlapAction.prototype.getClass = function getClass () {
104313           return MonotoneChainOverlapAction
104314         };
104315
104316         var MonotoneChain = function MonotoneChain () {
104317           this._pts = null;
104318           this._start = null;
104319           this._end = null;
104320           this._env = null;
104321           this._context = null;
104322           this._id = null;
104323           var pts = arguments[0];
104324           var start = arguments[1];
104325           var end = arguments[2];
104326           var context = arguments[3];
104327           this._pts = pts;
104328           this._start = start;
104329           this._end = end;
104330           this._context = context;
104331         };
104332         MonotoneChain.prototype.getLineSegment = function getLineSegment (index, ls) {
104333           ls.p0 = this._pts[index];
104334           ls.p1 = this._pts[index + 1];
104335         };
104336         MonotoneChain.prototype.computeSelect = function computeSelect (searchEnv, start0, end0, mcs) {
104337           var p0 = this._pts[start0];
104338           var p1 = this._pts[end0];
104339           mcs.tempEnv1.init(p0, p1);
104340           if (end0 - start0 === 1) {
104341             mcs.select(this, start0);
104342             return null
104343           }
104344           if (!searchEnv.intersects(mcs.tempEnv1)) { return null }
104345           var mid = Math.trunc((start0 + end0) / 2);
104346           if (start0 < mid) {
104347             this.computeSelect(searchEnv, start0, mid, mcs);
104348           }
104349           if (mid < end0) {
104350             this.computeSelect(searchEnv, mid, end0, mcs);
104351           }
104352         };
104353         MonotoneChain.prototype.getCoordinates = function getCoordinates () {
104354             var this$1 = this;
104355
104356           var coord = new Array(this._end - this._start + 1).fill(null);
104357           var index = 0;
104358           for (var i = this._start; i <= this._end; i++) {
104359             coord[index++] = this$1._pts[i];
104360           }
104361           return coord
104362         };
104363         MonotoneChain.prototype.computeOverlaps = function computeOverlaps (mc, mco) {
104364           this.computeOverlapsInternal(this._start, this._end, mc, mc._start, mc._end, mco);
104365         };
104366         MonotoneChain.prototype.setId = function setId (id) {
104367           this._id = id;
104368         };
104369         MonotoneChain.prototype.select = function select (searchEnv, mcs) {
104370           this.computeSelect(searchEnv, this._start, this._end, mcs);
104371         };
104372         MonotoneChain.prototype.getEnvelope = function getEnvelope () {
104373           if (this._env === null) {
104374             var p0 = this._pts[this._start];
104375             var p1 = this._pts[this._end];
104376             this._env = new Envelope(p0, p1);
104377           }
104378           return this._env
104379         };
104380         MonotoneChain.prototype.getEndIndex = function getEndIndex () {
104381           return this._end
104382         };
104383         MonotoneChain.prototype.getStartIndex = function getStartIndex () {
104384           return this._start
104385         };
104386         MonotoneChain.prototype.getContext = function getContext () {
104387           return this._context
104388         };
104389         MonotoneChain.prototype.getId = function getId () {
104390           return this._id
104391         };
104392         MonotoneChain.prototype.computeOverlapsInternal = function computeOverlapsInternal (start0, end0, mc, start1, end1, mco) {
104393           var p00 = this._pts[start0];
104394           var p01 = this._pts[end0];
104395           var p10 = mc._pts[start1];
104396           var p11 = mc._pts[end1];
104397           if (end0 - start0 === 1 && end1 - start1 === 1) {
104398             mco.overlap(this, start0, mc, start1);
104399             return null
104400           }
104401           mco.tempEnv1.init(p00, p01);
104402           mco.tempEnv2.init(p10, p11);
104403           if (!mco.tempEnv1.intersects(mco.tempEnv2)) { return null }
104404           var mid0 = Math.trunc((start0 + end0) / 2);
104405           var mid1 = Math.trunc((start1 + end1) / 2);
104406           if (start0 < mid0) {
104407             if (start1 < mid1) { this.computeOverlapsInternal(start0, mid0, mc, start1, mid1, mco); }
104408             if (mid1 < end1) { this.computeOverlapsInternal(start0, mid0, mc, mid1, end1, mco); }
104409           }
104410           if (mid0 < end0) {
104411             if (start1 < mid1) { this.computeOverlapsInternal(mid0, end0, mc, start1, mid1, mco); }
104412             if (mid1 < end1) { this.computeOverlapsInternal(mid0, end0, mc, mid1, end1, mco); }
104413           }
104414         };
104415         MonotoneChain.prototype.interfaces_ = function interfaces_ () {
104416           return []
104417         };
104418         MonotoneChain.prototype.getClass = function getClass () {
104419           return MonotoneChain
104420         };
104421
104422         var MonotoneChainBuilder = function MonotoneChainBuilder () {};
104423
104424         MonotoneChainBuilder.prototype.interfaces_ = function interfaces_ () {
104425           return []
104426         };
104427         MonotoneChainBuilder.prototype.getClass = function getClass () {
104428           return MonotoneChainBuilder
104429         };
104430         MonotoneChainBuilder.getChainStartIndices = function getChainStartIndices (pts) {
104431           var start = 0;
104432           var startIndexList = new ArrayList();
104433           startIndexList.add(new Integer(start));
104434           do {
104435             var last = MonotoneChainBuilder.findChainEnd(pts, start);
104436             startIndexList.add(new Integer(last));
104437             start = last;
104438           } while (start < pts.length - 1)
104439           var startIndex = MonotoneChainBuilder.toIntArray(startIndexList);
104440           return startIndex
104441         };
104442         MonotoneChainBuilder.findChainEnd = function findChainEnd (pts, start) {
104443           var safeStart = start;
104444           while (safeStart < pts.length - 1 && pts[safeStart].equals2D(pts[safeStart + 1])) {
104445             safeStart++;
104446           }
104447           if (safeStart >= pts.length - 1) {
104448             return pts.length - 1
104449           }
104450           var chainQuad = Quadrant.quadrant(pts[safeStart], pts[safeStart + 1]);
104451           var last = start + 1;
104452           while (last < pts.length) {
104453             if (!pts[last - 1].equals2D(pts[last])) {
104454               var quad = Quadrant.quadrant(pts[last - 1], pts[last]);
104455               if (quad !== chainQuad) { break }
104456             }
104457             last++;
104458           }
104459           return last - 1
104460         };
104461         MonotoneChainBuilder.getChains = function getChains () {
104462           if (arguments.length === 1) {
104463             var pts = arguments[0];
104464             return MonotoneChainBuilder.getChains(pts, null)
104465           } else if (arguments.length === 2) {
104466             var pts$1 = arguments[0];
104467             var context = arguments[1];
104468             var mcList = new ArrayList();
104469             var startIndex = MonotoneChainBuilder.getChainStartIndices(pts$1);
104470             for (var i = 0; i < startIndex.length - 1; i++) {
104471               var mc = new MonotoneChain(pts$1, startIndex[i], startIndex[i + 1], context);
104472               mcList.add(mc);
104473             }
104474             return mcList
104475           }
104476         };
104477         MonotoneChainBuilder.toIntArray = function toIntArray (list) {
104478           var array = new Array(list.size()).fill(null);
104479           for (var i = 0; i < array.length; i++) {
104480             array[i] = list.get(i).intValue();
104481           }
104482           return array
104483         };
104484
104485         var Noder = function Noder () {};
104486
104487         Noder.prototype.computeNodes = function computeNodes (segStrings) {};
104488         Noder.prototype.getNodedSubstrings = function getNodedSubstrings () {};
104489         Noder.prototype.interfaces_ = function interfaces_ () {
104490           return []
104491         };
104492         Noder.prototype.getClass = function getClass () {
104493           return Noder
104494         };
104495
104496         var SinglePassNoder = function SinglePassNoder () {
104497           this._segInt = null;
104498           if (arguments.length === 0) ; else if (arguments.length === 1) {
104499             var segInt = arguments[0];
104500             this.setSegmentIntersector(segInt);
104501           }
104502         };
104503         SinglePassNoder.prototype.setSegmentIntersector = function setSegmentIntersector (segInt) {
104504           this._segInt = segInt;
104505         };
104506         SinglePassNoder.prototype.interfaces_ = function interfaces_ () {
104507           return [Noder]
104508         };
104509         SinglePassNoder.prototype.getClass = function getClass () {
104510           return SinglePassNoder
104511         };
104512
104513         var MCIndexNoder = (function (SinglePassNoder$$1) {
104514           function MCIndexNoder (si) {
104515             if (si) { SinglePassNoder$$1.call(this, si); }
104516             else { SinglePassNoder$$1.call(this); }
104517             this._monoChains = new ArrayList();
104518             this._index = new STRtree();
104519             this._idCounter = 0;
104520             this._nodedSegStrings = null;
104521             this._nOverlaps = 0;
104522           }
104523
104524           if ( SinglePassNoder$$1 ) MCIndexNoder.__proto__ = SinglePassNoder$$1;
104525           MCIndexNoder.prototype = Object.create( SinglePassNoder$$1 && SinglePassNoder$$1.prototype );
104526           MCIndexNoder.prototype.constructor = MCIndexNoder;
104527
104528           var staticAccessors = { SegmentOverlapAction: { configurable: true } };
104529           MCIndexNoder.prototype.getMonotoneChains = function getMonotoneChains () {
104530             return this._monoChains
104531           };
104532           MCIndexNoder.prototype.getNodedSubstrings = function getNodedSubstrings () {
104533             return NodedSegmentString.getNodedSubstrings(this._nodedSegStrings)
104534           };
104535           MCIndexNoder.prototype.getIndex = function getIndex () {
104536             return this._index
104537           };
104538           MCIndexNoder.prototype.add = function add (segStr) {
104539             var this$1 = this;
104540
104541             var segChains = MonotoneChainBuilder.getChains(segStr.getCoordinates(), segStr);
104542             for (var i = segChains.iterator(); i.hasNext();) {
104543               var mc = i.next();
104544               mc.setId(this$1._idCounter++);
104545               this$1._index.insert(mc.getEnvelope(), mc);
104546               this$1._monoChains.add(mc);
104547             }
104548           };
104549           MCIndexNoder.prototype.computeNodes = function computeNodes (inputSegStrings) {
104550             var this$1 = this;
104551
104552             this._nodedSegStrings = inputSegStrings;
104553             for (var i = inputSegStrings.iterator(); i.hasNext();) {
104554               this$1.add(i.next());
104555             }
104556             this.intersectChains();
104557           };
104558           MCIndexNoder.prototype.intersectChains = function intersectChains () {
104559             var this$1 = this;
104560
104561             var overlapAction = new SegmentOverlapAction(this._segInt);
104562             for (var i = this._monoChains.iterator(); i.hasNext();) {
104563               var queryChain = i.next();
104564               var overlapChains = this$1._index.query(queryChain.getEnvelope());
104565               for (var j = overlapChains.iterator(); j.hasNext();) {
104566                 var testChain = j.next();
104567                 if (testChain.getId() > queryChain.getId()) {
104568                   queryChain.computeOverlaps(testChain, overlapAction);
104569                   this$1._nOverlaps++;
104570                 }
104571                 if (this$1._segInt.isDone()) { return null }
104572               }
104573             }
104574           };
104575           MCIndexNoder.prototype.interfaces_ = function interfaces_ () {
104576             return []
104577           };
104578           MCIndexNoder.prototype.getClass = function getClass () {
104579             return MCIndexNoder
104580           };
104581           staticAccessors.SegmentOverlapAction.get = function () { return SegmentOverlapAction };
104582
104583           Object.defineProperties( MCIndexNoder, staticAccessors );
104584
104585           return MCIndexNoder;
104586         }(SinglePassNoder));
104587
104588         var SegmentOverlapAction = (function (MonotoneChainOverlapAction$$1) {
104589           function SegmentOverlapAction () {
104590             MonotoneChainOverlapAction$$1.call(this);
104591             this._si = null;
104592             var si = arguments[0];
104593             this._si = si;
104594           }
104595
104596           if ( MonotoneChainOverlapAction$$1 ) SegmentOverlapAction.__proto__ = MonotoneChainOverlapAction$$1;
104597           SegmentOverlapAction.prototype = Object.create( MonotoneChainOverlapAction$$1 && MonotoneChainOverlapAction$$1.prototype );
104598           SegmentOverlapAction.prototype.constructor = SegmentOverlapAction;
104599           SegmentOverlapAction.prototype.overlap = function overlap () {
104600             if (arguments.length === 4) {
104601               var mc1 = arguments[0];
104602               var start1 = arguments[1];
104603               var mc2 = arguments[2];
104604               var start2 = arguments[3];
104605               var ss1 = mc1.getContext();
104606               var ss2 = mc2.getContext();
104607               this._si.processIntersections(ss1, start1, ss2, start2);
104608             } else { return MonotoneChainOverlapAction$$1.prototype.overlap.apply(this, arguments) }
104609           };
104610           SegmentOverlapAction.prototype.interfaces_ = function interfaces_ () {
104611             return []
104612           };
104613           SegmentOverlapAction.prototype.getClass = function getClass () {
104614             return SegmentOverlapAction
104615           };
104616
104617           return SegmentOverlapAction;
104618         }(MonotoneChainOverlapAction));
104619
104620         var BufferParameters = function BufferParameters () {
104621           this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS;
104622           this._endCapStyle = BufferParameters.CAP_ROUND;
104623           this._joinStyle = BufferParameters.JOIN_ROUND;
104624           this._mitreLimit = BufferParameters.DEFAULT_MITRE_LIMIT;
104625           this._isSingleSided = false;
104626           this._simplifyFactor = BufferParameters.DEFAULT_SIMPLIFY_FACTOR;
104627
104628           if (arguments.length === 0) ; else if (arguments.length === 1) {
104629             var quadrantSegments = arguments[0];
104630             this.setQuadrantSegments(quadrantSegments);
104631           } else if (arguments.length === 2) {
104632             var quadrantSegments$1 = arguments[0];
104633             var endCapStyle = arguments[1];
104634             this.setQuadrantSegments(quadrantSegments$1);
104635             this.setEndCapStyle(endCapStyle);
104636           } else if (arguments.length === 4) {
104637             var quadrantSegments$2 = arguments[0];
104638             var endCapStyle$1 = arguments[1];
104639             var joinStyle = arguments[2];
104640             var mitreLimit = arguments[3];
104641             this.setQuadrantSegments(quadrantSegments$2);
104642             this.setEndCapStyle(endCapStyle$1);
104643             this.setJoinStyle(joinStyle);
104644             this.setMitreLimit(mitreLimit);
104645           }
104646         };
104647
104648         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 } };
104649         BufferParameters.prototype.getEndCapStyle = function getEndCapStyle () {
104650           return this._endCapStyle
104651         };
104652         BufferParameters.prototype.isSingleSided = function isSingleSided () {
104653           return this._isSingleSided
104654         };
104655         BufferParameters.prototype.setQuadrantSegments = function setQuadrantSegments (quadSegs) {
104656           this._quadrantSegments = quadSegs;
104657           if (this._quadrantSegments === 0) { this._joinStyle = BufferParameters.JOIN_BEVEL; }
104658           if (this._quadrantSegments < 0) {
104659             this._joinStyle = BufferParameters.JOIN_MITRE;
104660             this._mitreLimit = Math.abs(this._quadrantSegments);
104661           }
104662           if (quadSegs <= 0) {
104663             this._quadrantSegments = 1;
104664           }
104665           if (this._joinStyle !== BufferParameters.JOIN_ROUND) {
104666             this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS;
104667           }
104668         };
104669         BufferParameters.prototype.getJoinStyle = function getJoinStyle () {
104670           return this._joinStyle
104671         };
104672         BufferParameters.prototype.setJoinStyle = function setJoinStyle (joinStyle) {
104673           this._joinStyle = joinStyle;
104674         };
104675         BufferParameters.prototype.setSimplifyFactor = function setSimplifyFactor (simplifyFactor) {
104676           this._simplifyFactor = simplifyFactor < 0 ? 0 : simplifyFactor;
104677         };
104678         BufferParameters.prototype.getSimplifyFactor = function getSimplifyFactor () {
104679           return this._simplifyFactor
104680         };
104681         BufferParameters.prototype.getQuadrantSegments = function getQuadrantSegments () {
104682           return this._quadrantSegments
104683         };
104684         BufferParameters.prototype.setEndCapStyle = function setEndCapStyle (endCapStyle) {
104685           this._endCapStyle = endCapStyle;
104686         };
104687         BufferParameters.prototype.getMitreLimit = function getMitreLimit () {
104688           return this._mitreLimit
104689         };
104690         BufferParameters.prototype.setMitreLimit = function setMitreLimit (mitreLimit) {
104691           this._mitreLimit = mitreLimit;
104692         };
104693         BufferParameters.prototype.setSingleSided = function setSingleSided (isSingleSided) {
104694           this._isSingleSided = isSingleSided;
104695         };
104696         BufferParameters.prototype.interfaces_ = function interfaces_ () {
104697           return []
104698         };
104699         BufferParameters.prototype.getClass = function getClass () {
104700           return BufferParameters
104701         };
104702         BufferParameters.bufferDistanceError = function bufferDistanceError (quadSegs) {
104703           var alpha = Math.PI / 2.0 / quadSegs;
104704           return 1 - Math.cos(alpha / 2.0)
104705         };
104706         staticAccessors$25.CAP_ROUND.get = function () { return 1 };
104707         staticAccessors$25.CAP_FLAT.get = function () { return 2 };
104708         staticAccessors$25.CAP_SQUARE.get = function () { return 3 };
104709         staticAccessors$25.JOIN_ROUND.get = function () { return 1 };
104710         staticAccessors$25.JOIN_MITRE.get = function () { return 2 };
104711         staticAccessors$25.JOIN_BEVEL.get = function () { return 3 };
104712         staticAccessors$25.DEFAULT_QUADRANT_SEGMENTS.get = function () { return 8 };
104713         staticAccessors$25.DEFAULT_MITRE_LIMIT.get = function () { return 5.0 };
104714         staticAccessors$25.DEFAULT_SIMPLIFY_FACTOR.get = function () { return 0.01 };
104715
104716         Object.defineProperties( BufferParameters, staticAccessors$25 );
104717
104718         var BufferInputLineSimplifier = function BufferInputLineSimplifier (inputLine) {
104719           this._distanceTol = null;
104720           this._isDeleted = null;
104721           this._angleOrientation = CGAlgorithms.COUNTERCLOCKWISE;
104722           this._inputLine = inputLine || null;
104723         };
104724
104725         var staticAccessors$26 = { INIT: { configurable: true },DELETE: { configurable: true },KEEP: { configurable: true },NUM_PTS_TO_CHECK: { configurable: true } };
104726         BufferInputLineSimplifier.prototype.isDeletable = function isDeletable (i0, i1, i2, distanceTol) {
104727           var p0 = this._inputLine[i0];
104728           var p1 = this._inputLine[i1];
104729           var p2 = this._inputLine[i2];
104730           if (!this.isConcave(p0, p1, p2)) { return false }
104731           if (!this.isShallow(p0, p1, p2, distanceTol)) { return false }
104732           return this.isShallowSampled(p0, p1, i0, i2, distanceTol)
104733         };
104734         BufferInputLineSimplifier.prototype.deleteShallowConcavities = function deleteShallowConcavities () {
104735             var this$1 = this;
104736
104737           var index = 1;
104738           // const maxIndex = this._inputLine.length - 1
104739           var midIndex = this.findNextNonDeletedIndex(index);
104740           var lastIndex = this.findNextNonDeletedIndex(midIndex);
104741           var isChanged = false;
104742           while (lastIndex < this._inputLine.length) {
104743             var isMiddleVertexDeleted = false;
104744             if (this$1.isDeletable(index, midIndex, lastIndex, this$1._distanceTol)) {
104745               this$1._isDeleted[midIndex] = BufferInputLineSimplifier.DELETE;
104746               isMiddleVertexDeleted = true;
104747               isChanged = true;
104748             }
104749             if (isMiddleVertexDeleted) { index = lastIndex; } else { index = midIndex; }
104750             midIndex = this$1.findNextNonDeletedIndex(index);
104751             lastIndex = this$1.findNextNonDeletedIndex(midIndex);
104752           }
104753           return isChanged
104754         };
104755         BufferInputLineSimplifier.prototype.isShallowConcavity = function isShallowConcavity (p0, p1, p2, distanceTol) {
104756           var orientation = CGAlgorithms.computeOrientation(p0, p1, p2);
104757           var isAngleToSimplify = orientation === this._angleOrientation;
104758           if (!isAngleToSimplify) { return false }
104759           var dist = CGAlgorithms.distancePointLine(p1, p0, p2);
104760           return dist < distanceTol
104761         };
104762         BufferInputLineSimplifier.prototype.isShallowSampled = function isShallowSampled (p0, p2, i0, i2, distanceTol) {
104763             var this$1 = this;
104764
104765           var inc = Math.trunc((i2 - i0) / BufferInputLineSimplifier.NUM_PTS_TO_CHECK);
104766           if (inc <= 0) { inc = 1; }
104767           for (var i = i0; i < i2; i += inc) {
104768             if (!this$1.isShallow(p0, p2, this$1._inputLine[i], distanceTol)) { return false }
104769           }
104770           return true
104771         };
104772         BufferInputLineSimplifier.prototype.isConcave = function isConcave (p0, p1, p2) {
104773           var orientation = CGAlgorithms.computeOrientation(p0, p1, p2);
104774           var isConcave = orientation === this._angleOrientation;
104775           return isConcave
104776         };
104777         BufferInputLineSimplifier.prototype.simplify = function simplify (distanceTol) {
104778             var this$1 = this;
104779
104780           this._distanceTol = Math.abs(distanceTol);
104781           if (distanceTol < 0) { this._angleOrientation = CGAlgorithms.CLOCKWISE; }
104782           this._isDeleted = new Array(this._inputLine.length).fill(null);
104783           var isChanged = false;
104784           do {
104785             isChanged = this$1.deleteShallowConcavities();
104786           } while (isChanged)
104787           return this.collapseLine()
104788         };
104789         BufferInputLineSimplifier.prototype.findNextNonDeletedIndex = function findNextNonDeletedIndex (index) {
104790           var next = index + 1;
104791           while (next < this._inputLine.length && this._isDeleted[next] === BufferInputLineSimplifier.DELETE) { next++; }
104792           return next
104793         };
104794         BufferInputLineSimplifier.prototype.isShallow = function isShallow (p0, p1, p2, distanceTol) {
104795           var dist = CGAlgorithms.distancePointLine(p1, p0, p2);
104796           return dist < distanceTol
104797         };
104798         BufferInputLineSimplifier.prototype.collapseLine = function collapseLine () {
104799             var this$1 = this;
104800
104801           var coordList = new CoordinateList();
104802           for (var i = 0; i < this._inputLine.length; i++) {
104803             if (this$1._isDeleted[i] !== BufferInputLineSimplifier.DELETE) { coordList.add(this$1._inputLine[i]); }
104804           }
104805           return coordList.toCoordinateArray()
104806         };
104807         BufferInputLineSimplifier.prototype.interfaces_ = function interfaces_ () {
104808           return []
104809         };
104810         BufferInputLineSimplifier.prototype.getClass = function getClass () {
104811           return BufferInputLineSimplifier
104812         };
104813         BufferInputLineSimplifier.simplify = function simplify (inputLine, distanceTol) {
104814           var simp = new BufferInputLineSimplifier(inputLine);
104815           return simp.simplify(distanceTol)
104816         };
104817         staticAccessors$26.INIT.get = function () { return 0 };
104818         staticAccessors$26.DELETE.get = function () { return 1 };
104819         staticAccessors$26.KEEP.get = function () { return 1 };
104820         staticAccessors$26.NUM_PTS_TO_CHECK.get = function () { return 10 };
104821
104822         Object.defineProperties( BufferInputLineSimplifier, staticAccessors$26 );
104823
104824         var OffsetSegmentString = function OffsetSegmentString () {
104825           this._ptList = null;
104826           this._precisionModel = null;
104827           this._minimimVertexDistance = 0.0;
104828           this._ptList = new ArrayList();
104829         };
104830
104831         var staticAccessors$28 = { COORDINATE_ARRAY_TYPE: { configurable: true } };
104832         OffsetSegmentString.prototype.getCoordinates = function getCoordinates () {
104833           var coord = this._ptList.toArray(OffsetSegmentString.COORDINATE_ARRAY_TYPE);
104834           return coord
104835         };
104836         OffsetSegmentString.prototype.setPrecisionModel = function setPrecisionModel (precisionModel) {
104837           this._precisionModel = precisionModel;
104838         };
104839         OffsetSegmentString.prototype.addPt = function addPt (pt) {
104840           var bufPt = new Coordinate(pt);
104841           this._precisionModel.makePrecise(bufPt);
104842           if (this.isRedundant(bufPt)) { return null }
104843           this._ptList.add(bufPt);
104844         };
104845         OffsetSegmentString.prototype.revere = function revere () {};
104846         OffsetSegmentString.prototype.addPts = function addPts (pt, isForward) {
104847             var this$1 = this;
104848
104849           if (isForward) {
104850             for (var i = 0; i < pt.length; i++) {
104851               this$1.addPt(pt[i]);
104852             }
104853           } else {
104854             for (var i$1 = pt.length - 1; i$1 >= 0; i$1--) {
104855               this$1.addPt(pt[i$1]);
104856             }
104857           }
104858         };
104859         OffsetSegmentString.prototype.isRedundant = function isRedundant (pt) {
104860           if (this._ptList.size() < 1) { return false }
104861           var lastPt = this._ptList.get(this._ptList.size() - 1);
104862           var ptDist = pt.distance(lastPt);
104863           if (ptDist < this._minimimVertexDistance) { return true }
104864           return false
104865         };
104866         OffsetSegmentString.prototype.toString = function toString () {
104867           var fact = new GeometryFactory();
104868           var line = fact.createLineString(this.getCoordinates());
104869           return line.toString()
104870         };
104871         OffsetSegmentString.prototype.closeRing = function closeRing () {
104872           if (this._ptList.size() < 1) { return null }
104873           var startPt = new Coordinate(this._ptList.get(0));
104874           var lastPt = this._ptList.get(this._ptList.size() - 1);
104875           // const last2Pt = null
104876           // if (this._ptList.size() >= 2) last2Pt = this._ptList.get(this._ptList.size() - 2)
104877           if (startPt.equals(lastPt)) { return null }
104878           this._ptList.add(startPt);
104879         };
104880         OffsetSegmentString.prototype.setMinimumVertexDistance = function setMinimumVertexDistance (minimimVertexDistance) {
104881           this._minimimVertexDistance = minimimVertexDistance;
104882         };
104883         OffsetSegmentString.prototype.interfaces_ = function interfaces_ () {
104884           return []
104885         };
104886         OffsetSegmentString.prototype.getClass = function getClass () {
104887           return OffsetSegmentString
104888         };
104889         staticAccessors$28.COORDINATE_ARRAY_TYPE.get = function () { return new Array(0).fill(null) };
104890
104891         Object.defineProperties( OffsetSegmentString, staticAccessors$28 );
104892
104893         var Angle = function Angle () {};
104894
104895         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 } };
104896
104897         Angle.prototype.interfaces_ = function interfaces_ () {
104898           return []
104899         };
104900         Angle.prototype.getClass = function getClass () {
104901           return Angle
104902         };
104903         Angle.toDegrees = function toDegrees (radians) {
104904           return radians * 180 / Math.PI
104905         };
104906         Angle.normalize = function normalize (angle) {
104907           while (angle > Math.PI) { angle -= Angle.PI_TIMES_2; }
104908           while (angle <= -Math.PI) { angle += Angle.PI_TIMES_2; }
104909           return angle
104910         };
104911         Angle.angle = function angle () {
104912           if (arguments.length === 1) {
104913             var p = arguments[0];
104914             return Math.atan2(p.y, p.x)
104915           } else if (arguments.length === 2) {
104916             var p0 = arguments[0];
104917             var p1 = arguments[1];
104918             var dx = p1.x - p0.x;
104919             var dy = p1.y - p0.y;
104920             return Math.atan2(dy, dx)
104921           }
104922         };
104923         Angle.isAcute = function isAcute (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.isObtuse = function isObtuse (p0, p1, p2) {
104932           var dx0 = p0.x - p1.x;
104933           var dy0 = p0.y - p1.y;
104934           var dx1 = p2.x - p1.x;
104935           var dy1 = p2.y - p1.y;
104936           var dotprod = dx0 * dx1 + dy0 * dy1;
104937           return dotprod < 0
104938         };
104939         Angle.interiorAngle = function interiorAngle (p0, p1, p2) {
104940           var anglePrev = Angle.angle(p1, p0);
104941           var angleNext = Angle.angle(p1, p2);
104942           return Math.abs(angleNext - anglePrev)
104943         };
104944         Angle.normalizePositive = function normalizePositive (angle) {
104945           if (angle < 0.0) {
104946             while (angle < 0.0) { angle += Angle.PI_TIMES_2; }
104947             if (angle >= Angle.PI_TIMES_2) { angle = 0.0; }
104948           } else {
104949             while (angle >= Angle.PI_TIMES_2) { angle -= Angle.PI_TIMES_2; }
104950             if (angle < 0.0) { angle = 0.0; }
104951           }
104952           return angle
104953         };
104954         Angle.angleBetween = function angleBetween (tip1, tail, tip2) {
104955           var a1 = Angle.angle(tail, tip1);
104956           var a2 = Angle.angle(tail, tip2);
104957           return Angle.diff(a1, a2)
104958         };
104959         Angle.diff = function diff (ang1, ang2) {
104960           var delAngle = null;
104961           if (ang1 < ang2) {
104962             delAngle = ang2 - ang1;
104963           } else {
104964             delAngle = ang1 - ang2;
104965           }
104966           if (delAngle > Math.PI) {
104967             delAngle = 2 * Math.PI - delAngle;
104968           }
104969           return delAngle
104970         };
104971         Angle.toRadians = function toRadians (angleDegrees) {
104972           return angleDegrees * Math.PI / 180.0
104973         };
104974         Angle.getTurn = function getTurn (ang1, ang2) {
104975           var crossproduct = Math.sin(ang2 - ang1);
104976           if (crossproduct > 0) {
104977             return Angle.COUNTERCLOCKWISE
104978           }
104979           if (crossproduct < 0) {
104980             return Angle.CLOCKWISE
104981           }
104982           return Angle.NONE
104983         };
104984         Angle.angleBetweenOriented = function angleBetweenOriented (tip1, tail, tip2) {
104985           var a1 = Angle.angle(tail, tip1);
104986           var a2 = Angle.angle(tail, tip2);
104987           var angDel = a2 - a1;
104988           if (angDel <= -Math.PI) { return angDel + Angle.PI_TIMES_2 }
104989           if (angDel > Math.PI) { return angDel - Angle.PI_TIMES_2 }
104990           return angDel
104991         };
104992         staticAccessors$29.PI_TIMES_2.get = function () { return 2.0 * Math.PI };
104993         staticAccessors$29.PI_OVER_2.get = function () { return Math.PI / 2.0 };
104994         staticAccessors$29.PI_OVER_4.get = function () { return Math.PI / 4.0 };
104995         staticAccessors$29.COUNTERCLOCKWISE.get = function () { return CGAlgorithms.COUNTERCLOCKWISE };
104996         staticAccessors$29.CLOCKWISE.get = function () { return CGAlgorithms.CLOCKWISE };
104997         staticAccessors$29.NONE.get = function () { return CGAlgorithms.COLLINEAR };
104998
104999         Object.defineProperties( Angle, staticAccessors$29 );
105000
105001         var OffsetSegmentGenerator = function OffsetSegmentGenerator () {
105002           this._maxCurveSegmentError = 0.0;
105003           this._filletAngleQuantum = null;
105004           this._closingSegLengthFactor = 1;
105005           this._segList = null;
105006           this._distance = 0.0;
105007           this._precisionModel = null;
105008           this._bufParams = null;
105009           this._li = null;
105010           this._s0 = null;
105011           this._s1 = null;
105012           this._s2 = null;
105013           this._seg0 = new LineSegment();
105014           this._seg1 = new LineSegment();
105015           this._offset0 = new LineSegment();
105016           this._offset1 = new LineSegment();
105017           this._side = 0;
105018           this._hasNarrowConcaveAngle = false;
105019           var precisionModel = arguments[0];
105020           var bufParams = arguments[1];
105021           var distance = arguments[2];
105022           this._precisionModel = precisionModel;
105023           this._bufParams = bufParams;
105024           this._li = new RobustLineIntersector();
105025           this._filletAngleQuantum = Math.PI / 2.0 / bufParams.getQuadrantSegments();
105026           if (bufParams.getQuadrantSegments() >= 8 && bufParams.getJoinStyle() === BufferParameters.JOIN_ROUND) { this._closingSegLengthFactor = OffsetSegmentGenerator.MAX_CLOSING_SEG_LEN_FACTOR; }
105027           this.init(distance);
105028         };
105029
105030         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 } };
105031         OffsetSegmentGenerator.prototype.addNextSegment = function addNextSegment (p, addStartPoint) {
105032           this._s0 = this._s1;
105033           this._s1 = this._s2;
105034           this._s2 = p;
105035           this._seg0.setCoordinates(this._s0, this._s1);
105036           this.computeOffsetSegment(this._seg0, this._side, this._distance, this._offset0);
105037           this._seg1.setCoordinates(this._s1, this._s2);
105038           this.computeOffsetSegment(this._seg1, this._side, this._distance, this._offset1);
105039           if (this._s1.equals(this._s2)) { return null }
105040           var orientation = CGAlgorithms.computeOrientation(this._s0, this._s1, this._s2);
105041           var outsideTurn = (orientation === CGAlgorithms.CLOCKWISE && this._side === Position.LEFT) || (orientation === CGAlgorithms.COUNTERCLOCKWISE && this._side === Position.RIGHT);
105042           if (orientation === 0) {
105043             this.addCollinear(addStartPoint);
105044           } else if (outsideTurn) {
105045             this.addOutsideTurn(orientation, addStartPoint);
105046           } else {
105047             this.addInsideTurn(orientation, addStartPoint);
105048           }
105049         };
105050         OffsetSegmentGenerator.prototype.addLineEndCap = function addLineEndCap (p0, p1) {
105051           var seg = new LineSegment(p0, p1);
105052           var offsetL = new LineSegment();
105053           this.computeOffsetSegment(seg, Position.LEFT, this._distance, offsetL);
105054           var offsetR = new LineSegment();
105055           this.computeOffsetSegment(seg, Position.RIGHT, this._distance, offsetR);
105056           var dx = p1.x - p0.x;
105057           var dy = p1.y - p0.y;
105058           var angle = Math.atan2(dy, dx);
105059           switch (this._bufParams.getEndCapStyle()) {
105060             case BufferParameters.CAP_ROUND:
105061               this._segList.addPt(offsetL.p1);
105062               this.addFilletArc(p1, angle + Math.PI / 2, angle - Math.PI / 2, CGAlgorithms.CLOCKWISE, this._distance);
105063               this._segList.addPt(offsetR.p1);
105064               break
105065             case BufferParameters.CAP_FLAT:
105066               this._segList.addPt(offsetL.p1);
105067               this._segList.addPt(offsetR.p1);
105068               break
105069             case BufferParameters.CAP_SQUARE:
105070               var squareCapSideOffset = new Coordinate();
105071               squareCapSideOffset.x = Math.abs(this._distance) * Math.cos(angle);
105072               squareCapSideOffset.y = Math.abs(this._distance) * Math.sin(angle);
105073               var squareCapLOffset = new Coordinate(offsetL.p1.x + squareCapSideOffset.x, offsetL.p1.y + squareCapSideOffset.y);
105074               var squareCapROffset = new Coordinate(offsetR.p1.x + squareCapSideOffset.x, offsetR.p1.y + squareCapSideOffset.y);
105075               this._segList.addPt(squareCapLOffset);
105076               this._segList.addPt(squareCapROffset);
105077               break
105078           }
105079         };
105080         OffsetSegmentGenerator.prototype.getCoordinates = function getCoordinates () {
105081           var pts = this._segList.getCoordinates();
105082           return pts
105083         };
105084         OffsetSegmentGenerator.prototype.addMitreJoin = function addMitreJoin (p, offset0, offset1, distance) {
105085           var isMitreWithinLimit = true;
105086           var intPt = null;
105087           try {
105088             intPt = HCoordinate.intersection(offset0.p0, offset0.p1, offset1.p0, offset1.p1);
105089             var mitreRatio = distance <= 0.0 ? 1.0 : intPt.distance(p) / Math.abs(distance);
105090             if (mitreRatio > this._bufParams.getMitreLimit()) { isMitreWithinLimit = false; }
105091           } catch (ex) {
105092             if (ex instanceof NotRepresentableException) {
105093               intPt = new Coordinate(0, 0);
105094               isMitreWithinLimit = false;
105095             } else { throw ex }
105096           } finally {}
105097           if (isMitreWithinLimit) {
105098             this._segList.addPt(intPt);
105099           } else {
105100             this.addLimitedMitreJoin(offset0, offset1, distance, this._bufParams.getMitreLimit());
105101           }
105102         };
105103         OffsetSegmentGenerator.prototype.addFilletCorner = function addFilletCorner (p, p0, p1, direction, radius) {
105104           var dx0 = p0.x - p.x;
105105           var dy0 = p0.y - p.y;
105106           var startAngle = Math.atan2(dy0, dx0);
105107           var dx1 = p1.x - p.x;
105108           var dy1 = p1.y - p.y;
105109           var endAngle = Math.atan2(dy1, dx1);
105110           if (direction === CGAlgorithms.CLOCKWISE) {
105111             if (startAngle <= endAngle) { startAngle += 2.0 * Math.PI; }
105112           } else {
105113             if (startAngle >= endAngle) { startAngle -= 2.0 * Math.PI; }
105114           }
105115           this._segList.addPt(p0);
105116           this.addFilletArc(p, startAngle, endAngle, direction, radius);
105117           this._segList.addPt(p1);
105118         };
105119         OffsetSegmentGenerator.prototype.addOutsideTurn = function addOutsideTurn (orientation, addStartPoint) {
105120           if (this._offset0.p1.distance(this._offset1.p0) < this._distance * OffsetSegmentGenerator.OFFSET_SEGMENT_SEPARATION_FACTOR) {
105121             this._segList.addPt(this._offset0.p1);
105122             return null
105123           }
105124           if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_MITRE) {
105125             this.addMitreJoin(this._s1, this._offset0, this._offset1, this._distance);
105126           } else if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_BEVEL) {
105127             this.addBevelJoin(this._offset0, this._offset1);
105128           } else {
105129             if (addStartPoint) { this._segList.addPt(this._offset0.p1); }
105130             this.addFilletCorner(this._s1, this._offset0.p1, this._offset1.p0, orientation, this._distance);
105131             this._segList.addPt(this._offset1.p0);
105132           }
105133         };
105134         OffsetSegmentGenerator.prototype.createSquare = function createSquare (p) {
105135           this._segList.addPt(new Coordinate(p.x + this._distance, p.y + this._distance));
105136           this._segList.addPt(new Coordinate(p.x + this._distance, p.y - this._distance));
105137           this._segList.addPt(new Coordinate(p.x - this._distance, p.y - this._distance));
105138           this._segList.addPt(new Coordinate(p.x - this._distance, p.y + this._distance));
105139           this._segList.closeRing();
105140         };
105141         OffsetSegmentGenerator.prototype.addSegments = function addSegments (pt, isForward) {
105142           this._segList.addPts(pt, isForward);
105143         };
105144         OffsetSegmentGenerator.prototype.addFirstSegment = function addFirstSegment () {
105145           this._segList.addPt(this._offset1.p0);
105146         };
105147         OffsetSegmentGenerator.prototype.addLastSegment = function addLastSegment () {
105148           this._segList.addPt(this._offset1.p1);
105149         };
105150         OffsetSegmentGenerator.prototype.initSideSegments = function initSideSegments (s1, s2, side) {
105151           this._s1 = s1;
105152           this._s2 = s2;
105153           this._side = side;
105154           this._seg1.setCoordinates(s1, s2);
105155           this.computeOffsetSegment(this._seg1, side, this._distance, this._offset1);
105156         };
105157         OffsetSegmentGenerator.prototype.addLimitedMitreJoin = function addLimitedMitreJoin (offset0, offset1, distance, mitreLimit) {
105158           var basePt = this._seg0.p1;
105159           var ang0 = Angle.angle(basePt, this._seg0.p0);
105160           // const ang1 = Angle.angle(basePt, this._seg1.p1)
105161           var angDiff = Angle.angleBetweenOriented(this._seg0.p0, basePt, this._seg1.p1);
105162           var angDiffHalf = angDiff / 2;
105163           var midAng = Angle.normalize(ang0 + angDiffHalf);
105164           var mitreMidAng = Angle.normalize(midAng + Math.PI);
105165           var mitreDist = mitreLimit * distance;
105166           var bevelDelta = mitreDist * Math.abs(Math.sin(angDiffHalf));
105167           var bevelHalfLen = distance - bevelDelta;
105168           var bevelMidX = basePt.x + mitreDist * Math.cos(mitreMidAng);
105169           var bevelMidY = basePt.y + mitreDist * Math.sin(mitreMidAng);
105170           var bevelMidPt = new Coordinate(bevelMidX, bevelMidY);
105171           var mitreMidLine = new LineSegment(basePt, bevelMidPt);
105172           var bevelEndLeft = mitreMidLine.pointAlongOffset(1.0, bevelHalfLen);
105173           var bevelEndRight = mitreMidLine.pointAlongOffset(1.0, -bevelHalfLen);
105174           if (this._side === Position.LEFT) {
105175             this._segList.addPt(bevelEndLeft);
105176             this._segList.addPt(bevelEndRight);
105177           } else {
105178             this._segList.addPt(bevelEndRight);
105179             this._segList.addPt(bevelEndLeft);
105180           }
105181         };
105182         OffsetSegmentGenerator.prototype.computeOffsetSegment = function computeOffsetSegment (seg, side, distance, offset) {
105183           var sideSign = side === Position.LEFT ? 1 : -1;
105184           var dx = seg.p1.x - seg.p0.x;
105185           var dy = seg.p1.y - seg.p0.y;
105186           var len = Math.sqrt(dx * dx + dy * dy);
105187           var ux = sideSign * distance * dx / len;
105188           var uy = sideSign * distance * dy / len;
105189           offset.p0.x = seg.p0.x - uy;
105190           offset.p0.y = seg.p0.y + ux;
105191           offset.p1.x = seg.p1.x - uy;
105192           offset.p1.y = seg.p1.y + ux;
105193         };
105194         OffsetSegmentGenerator.prototype.addFilletArc = function addFilletArc (p, startAngle, endAngle, direction, radius) {
105195             var this$1 = this;
105196
105197           var directionFactor = direction === CGAlgorithms.CLOCKWISE ? -1 : 1;
105198           var totalAngle = Math.abs(startAngle - endAngle);
105199           var nSegs = Math.trunc(totalAngle / this._filletAngleQuantum + 0.5);
105200           if (nSegs < 1) { return null }
105201           var initAngle = 0.0;
105202           var currAngleInc = totalAngle / nSegs;
105203           var currAngle = initAngle;
105204           var pt = new Coordinate();
105205           while (currAngle < totalAngle) {
105206             var angle = startAngle + directionFactor * currAngle;
105207             pt.x = p.x + radius * Math.cos(angle);
105208             pt.y = p.y + radius * Math.sin(angle);
105209             this$1._segList.addPt(pt);
105210             currAngle += currAngleInc;
105211           }
105212         };
105213         OffsetSegmentGenerator.prototype.addInsideTurn = function addInsideTurn (orientation, addStartPoint) {
105214           this._li.computeIntersection(this._offset0.p0, this._offset0.p1, this._offset1.p0, this._offset1.p1);
105215           if (this._li.hasIntersection()) {
105216             this._segList.addPt(this._li.getIntersection(0));
105217           } else {
105218             this._hasNarrowConcaveAngle = true;
105219             if (this._offset0.p1.distance(this._offset1.p0) < this._distance * OffsetSegmentGenerator.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR) {
105220               this._segList.addPt(this._offset0.p1);
105221             } else {
105222               this._segList.addPt(this._offset0.p1);
105223               if (this._closingSegLengthFactor > 0) {
105224                 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));
105225                 this._segList.addPt(mid0);
105226                 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));
105227                 this._segList.addPt(mid1);
105228               } else {
105229                 this._segList.addPt(this._s1);
105230               }
105231               this._segList.addPt(this._offset1.p0);
105232             }
105233           }
105234         };
105235         OffsetSegmentGenerator.prototype.createCircle = function createCircle (p) {
105236           var pt = new Coordinate(p.x + this._distance, p.y);
105237           this._segList.addPt(pt);
105238           this.addFilletArc(p, 0.0, 2.0 * Math.PI, -1, this._distance);
105239           this._segList.closeRing();
105240         };
105241         OffsetSegmentGenerator.prototype.addBevelJoin = function addBevelJoin (offset0, offset1) {
105242           this._segList.addPt(offset0.p1);
105243           this._segList.addPt(offset1.p0);
105244         };
105245         OffsetSegmentGenerator.prototype.init = function init (distance) {
105246           this._distance = distance;
105247           this._maxCurveSegmentError = distance * (1 - Math.cos(this._filletAngleQuantum / 2.0));
105248           this._segList = new OffsetSegmentString();
105249           this._segList.setPrecisionModel(this._precisionModel);
105250           this._segList.setMinimumVertexDistance(distance * OffsetSegmentGenerator.CURVE_VERTEX_SNAP_DISTANCE_FACTOR);
105251         };
105252         OffsetSegmentGenerator.prototype.addCollinear = function addCollinear (addStartPoint) {
105253           this._li.computeIntersection(this._s0, this._s1, this._s1, this._s2);
105254           var numInt = this._li.getIntersectionNum();
105255           if (numInt >= 2) {
105256             if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_BEVEL || this._bufParams.getJoinStyle() === BufferParameters.JOIN_MITRE) {
105257               if (addStartPoint) { this._segList.addPt(this._offset0.p1); }
105258               this._segList.addPt(this._offset1.p0);
105259             } else {
105260               this.addFilletCorner(this._s1, this._offset0.p1, this._offset1.p0, CGAlgorithms.CLOCKWISE, this._distance);
105261             }
105262           }
105263         };
105264         OffsetSegmentGenerator.prototype.closeRing = function closeRing () {
105265           this._segList.closeRing();
105266         };
105267         OffsetSegmentGenerator.prototype.hasNarrowConcaveAngle = function hasNarrowConcaveAngle () {
105268           return this._hasNarrowConcaveAngle
105269         };
105270         OffsetSegmentGenerator.prototype.interfaces_ = function interfaces_ () {
105271           return []
105272         };
105273         OffsetSegmentGenerator.prototype.getClass = function getClass () {
105274           return OffsetSegmentGenerator
105275         };
105276         staticAccessors$27.OFFSET_SEGMENT_SEPARATION_FACTOR.get = function () { return 1.0E-3 };
105277         staticAccessors$27.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR.get = function () { return 1.0E-3 };
105278         staticAccessors$27.CURVE_VERTEX_SNAP_DISTANCE_FACTOR.get = function () { return 1.0E-6 };
105279         staticAccessors$27.MAX_CLOSING_SEG_LEN_FACTOR.get = function () { return 80 };
105280
105281         Object.defineProperties( OffsetSegmentGenerator, staticAccessors$27 );
105282
105283         var OffsetCurveBuilder = function OffsetCurveBuilder () {
105284           this._distance = 0.0;
105285           this._precisionModel = null;
105286           this._bufParams = null;
105287           var precisionModel = arguments[0];
105288           var bufParams = arguments[1];
105289           this._precisionModel = precisionModel;
105290           this._bufParams = bufParams;
105291         };
105292         OffsetCurveBuilder.prototype.getOffsetCurve = function getOffsetCurve (inputPts, distance) {
105293           this._distance = distance;
105294           if (distance === 0.0) { return null }
105295           var isRightSide = distance < 0.0;
105296           var posDistance = Math.abs(distance);
105297           var segGen = this.getSegGen(posDistance);
105298           if (inputPts.length <= 1) {
105299             this.computePointCurve(inputPts[0], segGen);
105300           } else {
105301             this.computeOffsetCurve(inputPts, isRightSide, segGen);
105302           }
105303           var curvePts = segGen.getCoordinates();
105304           if (isRightSide) { CoordinateArrays.reverse(curvePts); }
105305           return curvePts
105306         };
105307         OffsetCurveBuilder.prototype.computeSingleSidedBufferCurve = function computeSingleSidedBufferCurve (inputPts, isRightSide, segGen) {
105308           var distTol = this.simplifyTolerance(this._distance);
105309           if (isRightSide) {
105310             segGen.addSegments(inputPts, true);
105311             var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105312             var n2 = simp2.length - 1;
105313             segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105314             segGen.addFirstSegment();
105315             for (var i = n2 - 2; i >= 0; i--) {
105316               segGen.addNextSegment(simp2[i], true);
105317             }
105318           } else {
105319             segGen.addSegments(inputPts, false);
105320             var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105321             var n1 = simp1.length - 1;
105322             segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105323             segGen.addFirstSegment();
105324             for (var i$1 = 2; i$1 <= n1; i$1++) {
105325               segGen.addNextSegment(simp1[i$1], true);
105326             }
105327           }
105328           segGen.addLastSegment();
105329           segGen.closeRing();
105330         };
105331         OffsetCurveBuilder.prototype.computeRingBufferCurve = function computeRingBufferCurve (inputPts, side, segGen) {
105332           var distTol = this.simplifyTolerance(this._distance);
105333           if (side === Position.RIGHT) { distTol = -distTol; }
105334           var simp = BufferInputLineSimplifier.simplify(inputPts, distTol);
105335           var n = simp.length - 1;
105336           segGen.initSideSegments(simp[n - 1], simp[0], side);
105337           for (var i = 1; i <= n; i++) {
105338             var addStartPoint = i !== 1;
105339             segGen.addNextSegment(simp[i], addStartPoint);
105340           }
105341           segGen.closeRing();
105342         };
105343         OffsetCurveBuilder.prototype.computeLineBufferCurve = function computeLineBufferCurve (inputPts, segGen) {
105344           var distTol = this.simplifyTolerance(this._distance);
105345           var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105346           var n1 = simp1.length - 1;
105347           segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105348           for (var i = 2; i <= n1; i++) {
105349             segGen.addNextSegment(simp1[i], true);
105350           }
105351           segGen.addLastSegment();
105352           segGen.addLineEndCap(simp1[n1 - 1], simp1[n1]);
105353           var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105354           var n2 = simp2.length - 1;
105355           segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105356           for (var i$1 = n2 - 2; i$1 >= 0; i$1--) {
105357             segGen.addNextSegment(simp2[i$1], true);
105358           }
105359           segGen.addLastSegment();
105360           segGen.addLineEndCap(simp2[1], simp2[0]);
105361           segGen.closeRing();
105362         };
105363         OffsetCurveBuilder.prototype.computePointCurve = function computePointCurve (pt, segGen) {
105364           switch (this._bufParams.getEndCapStyle()) {
105365             case BufferParameters.CAP_ROUND:
105366               segGen.createCircle(pt);
105367               break
105368             case BufferParameters.CAP_SQUARE:
105369               segGen.createSquare(pt);
105370               break
105371           }
105372         };
105373         OffsetCurveBuilder.prototype.getLineCurve = function getLineCurve (inputPts, distance) {
105374           this._distance = distance;
105375           if (distance < 0.0 && !this._bufParams.isSingleSided()) { return null }
105376           if (distance === 0.0) { return null }
105377           var posDistance = Math.abs(distance);
105378           var segGen = this.getSegGen(posDistance);
105379           if (inputPts.length <= 1) {
105380             this.computePointCurve(inputPts[0], segGen);
105381           } else {
105382             if (this._bufParams.isSingleSided()) {
105383               var isRightSide = distance < 0.0;
105384               this.computeSingleSidedBufferCurve(inputPts, isRightSide, segGen);
105385             } else { this.computeLineBufferCurve(inputPts, segGen); }
105386           }
105387           var lineCoord = segGen.getCoordinates();
105388           return lineCoord
105389         };
105390         OffsetCurveBuilder.prototype.getBufferParameters = function getBufferParameters () {
105391           return this._bufParams
105392         };
105393         OffsetCurveBuilder.prototype.simplifyTolerance = function simplifyTolerance (bufDistance) {
105394           return bufDistance * this._bufParams.getSimplifyFactor()
105395         };
105396         OffsetCurveBuilder.prototype.getRingCurve = function getRingCurve (inputPts, side, distance) {
105397           this._distance = distance;
105398           if (inputPts.length <= 2) { return this.getLineCurve(inputPts, distance) }
105399           if (distance === 0.0) {
105400             return OffsetCurveBuilder.copyCoordinates(inputPts)
105401           }
105402           var segGen = this.getSegGen(distance);
105403           this.computeRingBufferCurve(inputPts, side, segGen);
105404           return segGen.getCoordinates()
105405         };
105406         OffsetCurveBuilder.prototype.computeOffsetCurve = function computeOffsetCurve (inputPts, isRightSide, segGen) {
105407           var distTol = this.simplifyTolerance(this._distance);
105408           if (isRightSide) {
105409             var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105410             var n2 = simp2.length - 1;
105411             segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105412             segGen.addFirstSegment();
105413             for (var i = n2 - 2; i >= 0; i--) {
105414               segGen.addNextSegment(simp2[i], true);
105415             }
105416           } else {
105417             var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105418             var n1 = simp1.length - 1;
105419             segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105420             segGen.addFirstSegment();
105421             for (var i$1 = 2; i$1 <= n1; i$1++) {
105422               segGen.addNextSegment(simp1[i$1], true);
105423             }
105424           }
105425           segGen.addLastSegment();
105426         };
105427         OffsetCurveBuilder.prototype.getSegGen = function getSegGen (distance) {
105428           return new OffsetSegmentGenerator(this._precisionModel, this._bufParams, distance)
105429         };
105430         OffsetCurveBuilder.prototype.interfaces_ = function interfaces_ () {
105431           return []
105432         };
105433         OffsetCurveBuilder.prototype.getClass = function getClass () {
105434           return OffsetCurveBuilder
105435         };
105436         OffsetCurveBuilder.copyCoordinates = function copyCoordinates (pts) {
105437           var copy = new Array(pts.length).fill(null);
105438           for (var i = 0; i < copy.length; i++) {
105439             copy[i] = new Coordinate(pts[i]);
105440           }
105441           return copy
105442         };
105443
105444         var SubgraphDepthLocater = function SubgraphDepthLocater () {
105445           this._subgraphs = null;
105446           this._seg = new LineSegment();
105447           this._cga = new CGAlgorithms();
105448           var subgraphs = arguments[0];
105449           this._subgraphs = subgraphs;
105450         };
105451
105452         var staticAccessors$30 = { DepthSegment: { configurable: true } };
105453         SubgraphDepthLocater.prototype.findStabbedSegments = function findStabbedSegments () {
105454             var this$1 = this;
105455
105456           if (arguments.length === 1) {
105457             var stabbingRayLeftPt = arguments[0];
105458             var stabbedSegments = new ArrayList();
105459             for (var i = this._subgraphs.iterator(); i.hasNext();) {
105460               var bsg = i.next();
105461               var env = bsg.getEnvelope();
105462               if (stabbingRayLeftPt.y < env.getMinY() || stabbingRayLeftPt.y > env.getMaxY()) { continue }
105463               this$1.findStabbedSegments(stabbingRayLeftPt, bsg.getDirectedEdges(), stabbedSegments);
105464             }
105465             return stabbedSegments
105466           } else if (arguments.length === 3) {
105467             if (hasInterface(arguments[2], List) && (arguments[0] instanceof Coordinate && arguments[1] instanceof DirectedEdge)) {
105468               var stabbingRayLeftPt$1 = arguments[0];
105469               var dirEdge = arguments[1];
105470               var stabbedSegments$1 = arguments[2];
105471               var pts = dirEdge.getEdge().getCoordinates();
105472               for (var i$1 = 0; i$1 < pts.length - 1; i$1++) {
105473                 this$1._seg.p0 = pts[i$1];
105474                 this$1._seg.p1 = pts[i$1 + 1];
105475                 if (this$1._seg.p0.y > this$1._seg.p1.y) { this$1._seg.reverse(); }
105476                 var maxx = Math.max(this$1._seg.p0.x, this$1._seg.p1.x);
105477                 if (maxx < stabbingRayLeftPt$1.x) { continue }
105478                 if (this$1._seg.isHorizontal()) { continue }
105479                 if (stabbingRayLeftPt$1.y < this$1._seg.p0.y || stabbingRayLeftPt$1.y > this$1._seg.p1.y) { continue }
105480                 if (CGAlgorithms.computeOrientation(this$1._seg.p0, this$1._seg.p1, stabbingRayLeftPt$1) === CGAlgorithms.RIGHT) { continue }
105481                 var depth = dirEdge.getDepth(Position.LEFT);
105482                 if (!this$1._seg.p0.equals(pts[i$1])) { depth = dirEdge.getDepth(Position.RIGHT); }
105483                 var ds = new DepthSegment(this$1._seg, depth);
105484                 stabbedSegments$1.add(ds);
105485               }
105486             } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Coordinate && hasInterface(arguments[1], List))) {
105487               var stabbingRayLeftPt$2 = arguments[0];
105488               var dirEdges = arguments[1];
105489               var stabbedSegments$2 = arguments[2];
105490               for (var i$2 = dirEdges.iterator(); i$2.hasNext();) {
105491                 var de = i$2.next();
105492                 if (!de.isForward()) { continue }
105493                 this$1.findStabbedSegments(stabbingRayLeftPt$2, de, stabbedSegments$2);
105494               }
105495             }
105496           }
105497         };
105498         SubgraphDepthLocater.prototype.getDepth = function getDepth (p) {
105499           var stabbedSegments = this.findStabbedSegments(p);
105500           if (stabbedSegments.size() === 0) { return 0 }
105501           var ds = Collections.min(stabbedSegments);
105502           return ds._leftDepth
105503         };
105504         SubgraphDepthLocater.prototype.interfaces_ = function interfaces_ () {
105505           return []
105506         };
105507         SubgraphDepthLocater.prototype.getClass = function getClass () {
105508           return SubgraphDepthLocater
105509         };
105510         staticAccessors$30.DepthSegment.get = function () { return DepthSegment };
105511
105512         Object.defineProperties( SubgraphDepthLocater, staticAccessors$30 );
105513
105514         var DepthSegment = function DepthSegment () {
105515           this._upwardSeg = null;
105516           this._leftDepth = null;
105517           var seg = arguments[0];
105518           var depth = arguments[1];
105519           this._upwardSeg = new LineSegment(seg);
105520           this._leftDepth = depth;
105521         };
105522         DepthSegment.prototype.compareTo = function compareTo (obj) {
105523           var other = obj;
105524           if (this._upwardSeg.minX() >= other._upwardSeg.maxX()) { return 1 }
105525           if (this._upwardSeg.maxX() <= other._upwardSeg.minX()) { return -1 }
105526           var orientIndex = this._upwardSeg.orientationIndex(other._upwardSeg);
105527           if (orientIndex !== 0) { return orientIndex }
105528           orientIndex = -1 * other._upwardSeg.orientationIndex(this._upwardSeg);
105529           if (orientIndex !== 0) { return orientIndex }
105530           return this._upwardSeg.compareTo(other._upwardSeg)
105531         };
105532         DepthSegment.prototype.compareX = function compareX (seg0, seg1) {
105533           var compare0 = seg0.p0.compareTo(seg1.p0);
105534           if (compare0 !== 0) { return compare0 }
105535           return seg0.p1.compareTo(seg1.p1)
105536         };
105537         DepthSegment.prototype.toString = function toString () {
105538           return this._upwardSeg.toString()
105539         };
105540         DepthSegment.prototype.interfaces_ = function interfaces_ () {
105541           return [Comparable]
105542         };
105543         DepthSegment.prototype.getClass = function getClass () {
105544           return DepthSegment
105545         };
105546
105547         var Triangle = function Triangle (p0, p1, p2) {
105548           this.p0 = p0 || null;
105549           this.p1 = p1 || null;
105550           this.p2 = p2 || null;
105551         };
105552         Triangle.prototype.area = function area () {
105553           return Triangle.area(this.p0, this.p1, this.p2)
105554         };
105555         Triangle.prototype.signedArea = function signedArea () {
105556           return Triangle.signedArea(this.p0, this.p1, this.p2)
105557         };
105558         Triangle.prototype.interpolateZ = function interpolateZ (p) {
105559           if (p === null) { throw new IllegalArgumentException('Supplied point is null.') }
105560           return Triangle.interpolateZ(p, this.p0, this.p1, this.p2)
105561         };
105562         Triangle.prototype.longestSideLength = function longestSideLength () {
105563           return Triangle.longestSideLength(this.p0, this.p1, this.p2)
105564         };
105565         Triangle.prototype.isAcute = function isAcute () {
105566           return Triangle.isAcute(this.p0, this.p1, this.p2)
105567         };
105568         Triangle.prototype.circumcentre = function circumcentre () {
105569           return Triangle.circumcentre(this.p0, this.p1, this.p2)
105570         };
105571         Triangle.prototype.area3D = function area3D () {
105572           return Triangle.area3D(this.p0, this.p1, this.p2)
105573         };
105574         Triangle.prototype.centroid = function centroid () {
105575           return Triangle.centroid(this.p0, this.p1, this.p2)
105576         };
105577         Triangle.prototype.inCentre = function inCentre () {
105578           return Triangle.inCentre(this.p0, this.p1, this.p2)
105579         };
105580         Triangle.prototype.interfaces_ = function interfaces_ () {
105581           return []
105582         };
105583         Triangle.prototype.getClass = function getClass () {
105584           return Triangle
105585         };
105586         Triangle.area = function area (a, b, c) {
105587           return Math.abs(((c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y)) / 2)
105588         };
105589         Triangle.signedArea = function signedArea (a, b, c) {
105590           return ((c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y)) / 2
105591         };
105592         Triangle.det = function det (m00, m01, m10, m11) {
105593           return m00 * m11 - m01 * m10
105594         };
105595         Triangle.interpolateZ = function interpolateZ (p, v0, v1, v2) {
105596           var x0 = v0.x;
105597           var y0 = v0.y;
105598           var a = v1.x - x0;
105599           var b = v2.x - x0;
105600           var c = v1.y - y0;
105601           var d = v2.y - y0;
105602           var det = a * d - b * c;
105603           var dx = p.x - x0;
105604           var dy = p.y - y0;
105605           var t = (d * dx - b * dy) / det;
105606           var u = (-c * dx + a * dy) / det;
105607           var z = v0.z + t * (v1.z - v0.z) + u * (v2.z - v0.z);
105608           return z
105609         };
105610         Triangle.longestSideLength = function longestSideLength (a, b, c) {
105611           var lenAB = a.distance(b);
105612           var lenBC = b.distance(c);
105613           var lenCA = c.distance(a);
105614           var maxLen = lenAB;
105615           if (lenBC > maxLen) { maxLen = lenBC; }
105616           if (lenCA > maxLen) { maxLen = lenCA; }
105617           return maxLen
105618         };
105619         Triangle.isAcute = function isAcute (a, b, c) {
105620           if (!Angle.isAcute(a, b, c)) { return false }
105621           if (!Angle.isAcute(b, c, a)) { return false }
105622           if (!Angle.isAcute(c, a, b)) { return false }
105623           return true
105624         };
105625         Triangle.circumcentre = function circumcentre (a, b, c) {
105626           var cx = c.x;
105627           var cy = c.y;
105628           var ax = a.x - cx;
105629           var ay = a.y - cy;
105630           var bx = b.x - cx;
105631           var by = b.y - cy;
105632           var denom = 2 * Triangle.det(ax, ay, bx, by);
105633           var numx = Triangle.det(ay, ax * ax + ay * ay, by, bx * bx + by * by);
105634           var numy = Triangle.det(ax, ax * ax + ay * ay, bx, bx * bx + by * by);
105635           var ccx = cx - numx / denom;
105636           var ccy = cy + numy / denom;
105637           return new Coordinate(ccx, ccy)
105638         };
105639         Triangle.perpendicularBisector = function perpendicularBisector (a, b) {
105640           var dx = b.x - a.x;
105641           var dy = b.y - a.y;
105642           var l1 = new HCoordinate(a.x + dx / 2.0, a.y + dy / 2.0, 1.0);
105643           var l2 = new HCoordinate(a.x - dy + dx / 2.0, a.y + dx + dy / 2.0, 1.0);
105644           return new HCoordinate(l1, l2)
105645         };
105646         Triangle.angleBisector = function angleBisector (a, b, c) {
105647           var len0 = b.distance(a);
105648           var len2 = b.distance(c);
105649           var frac = len0 / (len0 + len2);
105650           var dx = c.x - a.x;
105651           var dy = c.y - a.y;
105652           var splitPt = new Coordinate(a.x + frac * dx, a.y + frac * dy);
105653           return splitPt
105654         };
105655         Triangle.area3D = function area3D (a, b, c) {
105656           var ux = b.x - a.x;
105657           var uy = b.y - a.y;
105658           var uz = b.z - a.z;
105659           var vx = c.x - a.x;
105660           var vy = c.y - a.y;
105661           var vz = c.z - a.z;
105662           var crossx = uy * vz - uz * vy;
105663           var crossy = uz * vx - ux * vz;
105664           var crossz = ux * vy - uy * vx;
105665           var absSq = crossx * crossx + crossy * crossy + crossz * crossz;
105666           var area3D = Math.sqrt(absSq) / 2;
105667           return area3D
105668         };
105669         Triangle.centroid = function centroid (a, b, c) {
105670           var x = (a.x + b.x + c.x) / 3;
105671           var y = (a.y + b.y + c.y) / 3;
105672           return new Coordinate(x, y)
105673         };
105674         Triangle.inCentre = function inCentre (a, b, c) {
105675           var len0 = b.distance(c);
105676           var len1 = a.distance(c);
105677           var len2 = a.distance(b);
105678           var circum = len0 + len1 + len2;
105679           var inCentreX = (len0 * a.x + len1 * b.x + len2 * c.x) / circum;
105680           var inCentreY = (len0 * a.y + len1 * b.y + len2 * c.y) / circum;
105681           return new Coordinate(inCentreX, inCentreY)
105682         };
105683
105684         var OffsetCurveSetBuilder = function OffsetCurveSetBuilder () {
105685           this._inputGeom = null;
105686           this._distance = null;
105687           this._curveBuilder = null;
105688           this._curveList = new ArrayList();
105689           var inputGeom = arguments[0];
105690           var distance = arguments[1];
105691           var curveBuilder = arguments[2];
105692           this._inputGeom = inputGeom;
105693           this._distance = distance;
105694           this._curveBuilder = curveBuilder;
105695         };
105696         OffsetCurveSetBuilder.prototype.addPoint = function addPoint (p) {
105697           if (this._distance <= 0.0) { return null }
105698           var coord = p.getCoordinates();
105699           var curve = this._curveBuilder.getLineCurve(coord, this._distance);
105700           this.addCurve(curve, Location.EXTERIOR, Location.INTERIOR);
105701         };
105702         OffsetCurveSetBuilder.prototype.addPolygon = function addPolygon (p) {
105703             var this$1 = this;
105704
105705           var offsetDistance = this._distance;
105706           var offsetSide = Position.LEFT;
105707           if (this._distance < 0.0) {
105708             offsetDistance = -this._distance;
105709             offsetSide = Position.RIGHT;
105710           }
105711           var shell = p.getExteriorRing();
105712           var shellCoord = CoordinateArrays.removeRepeatedPoints(shell.getCoordinates());
105713           if (this._distance < 0.0 && this.isErodedCompletely(shell, this._distance)) { return null }
105714           if (this._distance <= 0.0 && shellCoord.length < 3) { return null }
105715           this.addPolygonRing(shellCoord, offsetDistance, offsetSide, Location.EXTERIOR, Location.INTERIOR);
105716           for (var i = 0; i < p.getNumInteriorRing(); i++) {
105717             var hole = p.getInteriorRingN(i);
105718             var holeCoord = CoordinateArrays.removeRepeatedPoints(hole.getCoordinates());
105719             if (this$1._distance > 0.0 && this$1.isErodedCompletely(hole, -this$1._distance)) { continue }
105720             this$1.addPolygonRing(holeCoord, offsetDistance, Position.opposite(offsetSide), Location.INTERIOR, Location.EXTERIOR);
105721           }
105722         };
105723         OffsetCurveSetBuilder.prototype.isTriangleErodedCompletely = function isTriangleErodedCompletely (triangleCoord, bufferDistance) {
105724           var tri = new Triangle(triangleCoord[0], triangleCoord[1], triangleCoord[2]);
105725           var inCentre = tri.inCentre();
105726           var distToCentre = CGAlgorithms.distancePointLine(inCentre, tri.p0, tri.p1);
105727           return distToCentre < Math.abs(bufferDistance)
105728         };
105729         OffsetCurveSetBuilder.prototype.addLineString = function addLineString (line) {
105730           if (this._distance <= 0.0 && !this._curveBuilder.getBufferParameters().isSingleSided()) { return null }
105731           var coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
105732           var curve = this._curveBuilder.getLineCurve(coord, this._distance);
105733           this.addCurve(curve, Location.EXTERIOR, Location.INTERIOR);
105734         };
105735         OffsetCurveSetBuilder.prototype.addCurve = function addCurve (coord, leftLoc, rightLoc) {
105736           if (coord === null || coord.length < 2) { return null }
105737           var e = new NodedSegmentString(coord, new Label(0, Location.BOUNDARY, leftLoc, rightLoc));
105738           this._curveList.add(e);
105739         };
105740         OffsetCurveSetBuilder.prototype.getCurves = function getCurves () {
105741           this.add(this._inputGeom);
105742           return this._curveList
105743         };
105744         OffsetCurveSetBuilder.prototype.addPolygonRing = function addPolygonRing (coord, offsetDistance, side, cwLeftLoc, cwRightLoc) {
105745           if (offsetDistance === 0.0 && coord.length < LinearRing.MINIMUM_VALID_SIZE) { return null }
105746           var leftLoc = cwLeftLoc;
105747           var rightLoc = cwRightLoc;
105748           if (coord.length >= LinearRing.MINIMUM_VALID_SIZE && CGAlgorithms.isCCW(coord)) {
105749             leftLoc = cwRightLoc;
105750             rightLoc = cwLeftLoc;
105751             side = Position.opposite(side);
105752           }
105753           var curve = this._curveBuilder.getRingCurve(coord, side, offsetDistance);
105754           this.addCurve(curve, leftLoc, rightLoc);
105755         };
105756         OffsetCurveSetBuilder.prototype.add = function add (g) {
105757           if (g.isEmpty()) { return null }
105758           if (g instanceof Polygon) { this.addPolygon(g); }
105759           else if (g instanceof LineString) { this.addLineString(g); }
105760           else if (g instanceof Point$1) { this.addPoint(g); }
105761           else if (g instanceof MultiPoint) { this.addCollection(g); }
105762           else if (g instanceof MultiLineString) { this.addCollection(g); }
105763           else if (g instanceof MultiPolygon) { this.addCollection(g); }
105764           else if (g instanceof GeometryCollection) { this.addCollection(g); }
105765           // else throw new UnsupportedOperationException(g.getClass().getName())
105766         };
105767         OffsetCurveSetBuilder.prototype.isErodedCompletely = function isErodedCompletely (ring, bufferDistance) {
105768           var ringCoord = ring.getCoordinates();
105769           // const minDiam = 0.0
105770           if (ringCoord.length < 4) { return bufferDistance < 0 }
105771           if (ringCoord.length === 4) { return this.isTriangleErodedCompletely(ringCoord, bufferDistance) }
105772           var env = ring.getEnvelopeInternal();
105773           var envMinDimension = Math.min(env.getHeight(), env.getWidth());
105774           if (bufferDistance < 0.0 && 2 * Math.abs(bufferDistance) > envMinDimension) { return true }
105775           return false
105776         };
105777         OffsetCurveSetBuilder.prototype.addCollection = function addCollection (gc) {
105778             var this$1 = this;
105779
105780           for (var i = 0; i < gc.getNumGeometries(); i++) {
105781             var g = gc.getGeometryN(i);
105782             this$1.add(g);
105783           }
105784         };
105785         OffsetCurveSetBuilder.prototype.interfaces_ = function interfaces_ () {
105786           return []
105787         };
105788         OffsetCurveSetBuilder.prototype.getClass = function getClass () {
105789           return OffsetCurveSetBuilder
105790         };
105791
105792         var PointOnGeometryLocator = function PointOnGeometryLocator () {};
105793
105794         PointOnGeometryLocator.prototype.locate = function locate (p) {};
105795         PointOnGeometryLocator.prototype.interfaces_ = function interfaces_ () {
105796           return []
105797         };
105798         PointOnGeometryLocator.prototype.getClass = function getClass () {
105799           return PointOnGeometryLocator
105800         };
105801
105802         var GeometryCollectionIterator = function GeometryCollectionIterator () {
105803           this._parent = null;
105804           this._atStart = null;
105805           this._max = null;
105806           this._index = null;
105807           this._subcollectionIterator = null;
105808           var parent = arguments[0];
105809           this._parent = parent;
105810           this._atStart = true;
105811           this._index = 0;
105812           this._max = parent.getNumGeometries();
105813         };
105814         GeometryCollectionIterator.prototype.next = function next () {
105815           if (this._atStart) {
105816             this._atStart = false;
105817             if (GeometryCollectionIterator.isAtomic(this._parent)) { this._index++; }
105818             return this._parent
105819           }
105820           if (this._subcollectionIterator !== null) {
105821             if (this._subcollectionIterator.hasNext()) {
105822               return this._subcollectionIterator.next()
105823             } else {
105824               this._subcollectionIterator = null;
105825             }
105826           }
105827           if (this._index >= this._max) {
105828             throw new NoSuchElementException()
105829           }
105830           var obj = this._parent.getGeometryN(this._index++);
105831           if (obj instanceof GeometryCollection) {
105832             this._subcollectionIterator = new GeometryCollectionIterator(obj);
105833             return this._subcollectionIterator.next()
105834           }
105835           return obj
105836         };
105837         GeometryCollectionIterator.prototype.remove = function remove () {
105838           throw new Error(this.getClass().getName())
105839         };
105840         GeometryCollectionIterator.prototype.hasNext = function hasNext () {
105841           if (this._atStart) {
105842             return true
105843           }
105844           if (this._subcollectionIterator !== null) {
105845             if (this._subcollectionIterator.hasNext()) {
105846               return true
105847             }
105848             this._subcollectionIterator = null;
105849           }
105850           if (this._index >= this._max) {
105851             return false
105852           }
105853           return true
105854         };
105855         GeometryCollectionIterator.prototype.interfaces_ = function interfaces_ () {
105856           return [Iterator$1]
105857         };
105858         GeometryCollectionIterator.prototype.getClass = function getClass () {
105859           return GeometryCollectionIterator
105860         };
105861         GeometryCollectionIterator.isAtomic = function isAtomic (geom) {
105862           return !(geom instanceof GeometryCollection)
105863         };
105864
105865         var SimplePointInAreaLocator = function SimplePointInAreaLocator () {
105866           this._geom = null;
105867           var geom = arguments[0];
105868           this._geom = geom;
105869         };
105870         SimplePointInAreaLocator.prototype.locate = function locate (p) {
105871           return SimplePointInAreaLocator.locate(p, this._geom)
105872         };
105873         SimplePointInAreaLocator.prototype.interfaces_ = function interfaces_ () {
105874           return [PointOnGeometryLocator]
105875         };
105876         SimplePointInAreaLocator.prototype.getClass = function getClass () {
105877           return SimplePointInAreaLocator
105878         };
105879         SimplePointInAreaLocator.isPointInRing = function isPointInRing (p, ring) {
105880           if (!ring.getEnvelopeInternal().intersects(p)) { return false }
105881           return CGAlgorithms.isPointInRing(p, ring.getCoordinates())
105882         };
105883         SimplePointInAreaLocator.containsPointInPolygon = function containsPointInPolygon (p, poly) {
105884           if (poly.isEmpty()) { return false }
105885           var shell = poly.getExteriorRing();
105886           if (!SimplePointInAreaLocator.isPointInRing(p, shell)) { return false }
105887           for (var i = 0; i < poly.getNumInteriorRing(); i++) {
105888             var hole = poly.getInteriorRingN(i);
105889             if (SimplePointInAreaLocator.isPointInRing(p, hole)) { return false }
105890           }
105891           return true
105892         };
105893         SimplePointInAreaLocator.containsPoint = function containsPoint (p, geom) {
105894           if (geom instanceof Polygon) {
105895             return SimplePointInAreaLocator.containsPointInPolygon(p, geom)
105896           } else if (geom instanceof GeometryCollection) {
105897             var geomi = new GeometryCollectionIterator(geom);
105898             while (geomi.hasNext()) {
105899               var g2 = geomi.next();
105900               if (g2 !== geom) { if (SimplePointInAreaLocator.containsPoint(p, g2)) { return true } }
105901             }
105902           }
105903           return false
105904         };
105905         SimplePointInAreaLocator.locate = function locate (p, geom) {
105906           if (geom.isEmpty()) { return Location.EXTERIOR }
105907           if (SimplePointInAreaLocator.containsPoint(p, geom)) { return Location.INTERIOR }
105908           return Location.EXTERIOR
105909         };
105910
105911         var EdgeEndStar = function EdgeEndStar () {
105912           this._edgeMap = new TreeMap();
105913           this._edgeList = null;
105914           this._ptInAreaLocation = [Location.NONE, Location.NONE];
105915         };
105916         EdgeEndStar.prototype.getNextCW = function getNextCW (ee) {
105917           this.getEdges();
105918           var i = this._edgeList.indexOf(ee);
105919           var iNextCW = i - 1;
105920           if (i === 0) { iNextCW = this._edgeList.size() - 1; }
105921           return this._edgeList.get(iNextCW)
105922         };
105923         EdgeEndStar.prototype.propagateSideLabels = function propagateSideLabels (geomIndex) {
105924           var startLoc = Location.NONE;
105925           for (var it = this.iterator(); it.hasNext();) {
105926             var e = it.next();
105927             var label = e.getLabel();
105928             if (label.isArea(geomIndex) && label.getLocation(geomIndex, Position.LEFT) !== Location.NONE) { startLoc = label.getLocation(geomIndex, Position.LEFT); }
105929           }
105930           if (startLoc === Location.NONE) { return null }
105931           var currLoc = startLoc;
105932           for (var it$1 = this.iterator(); it$1.hasNext();) {
105933             var e$1 = it$1.next();
105934             var label$1 = e$1.getLabel();
105935             if (label$1.getLocation(geomIndex, Position.ON) === Location.NONE) { label$1.setLocation(geomIndex, Position.ON, currLoc); }
105936             if (label$1.isArea(geomIndex)) {
105937               var leftLoc = label$1.getLocation(geomIndex, Position.LEFT);
105938               var rightLoc = label$1.getLocation(geomIndex, Position.RIGHT);
105939               if (rightLoc !== Location.NONE) {
105940                 if (rightLoc !== currLoc) { throw new TopologyException('side location conflict', e$1.getCoordinate()) }
105941                 if (leftLoc === Location.NONE) {
105942                   Assert.shouldNeverReachHere('found single null side (at ' + e$1.getCoordinate() + ')');
105943                 }
105944                 currLoc = leftLoc;
105945               } else {
105946                 Assert.isTrue(label$1.getLocation(geomIndex, Position.LEFT) === Location.NONE, 'found single null side');
105947                 label$1.setLocation(geomIndex, Position.RIGHT, currLoc);
105948                 label$1.setLocation(geomIndex, Position.LEFT, currLoc);
105949               }
105950             }
105951           }
105952         };
105953         EdgeEndStar.prototype.getCoordinate = function getCoordinate () {
105954           var it = this.iterator();
105955           if (!it.hasNext()) { return null }
105956           var e = it.next();
105957           return e.getCoordinate()
105958         };
105959         EdgeEndStar.prototype.print = function print (out) {
105960           System.out.println('EdgeEndStar:   ' + this.getCoordinate());
105961           for (var it = this.iterator(); it.hasNext();) {
105962             var e = it.next();
105963             e.print(out);
105964           }
105965         };
105966         EdgeEndStar.prototype.isAreaLabelsConsistent = function isAreaLabelsConsistent (geomGraph) {
105967           this.computeEdgeEndLabels(geomGraph.getBoundaryNodeRule());
105968           return this.checkAreaLabelsConsistent(0)
105969         };
105970         EdgeEndStar.prototype.checkAreaLabelsConsistent = function checkAreaLabelsConsistent (geomIndex) {
105971           var edges = this.getEdges();
105972           if (edges.size() <= 0) { return true }
105973           var lastEdgeIndex = edges.size() - 1;
105974           var startLabel = edges.get(lastEdgeIndex).getLabel();
105975           var startLoc = startLabel.getLocation(geomIndex, Position.LEFT);
105976           Assert.isTrue(startLoc !== Location.NONE, 'Found unlabelled area edge');
105977           var currLoc = startLoc;
105978           for (var it = this.iterator(); it.hasNext();) {
105979             var e = it.next();
105980             var label = e.getLabel();
105981             Assert.isTrue(label.isArea(geomIndex), 'Found non-area edge');
105982             var leftLoc = label.getLocation(geomIndex, Position.LEFT);
105983             var rightLoc = label.getLocation(geomIndex, Position.RIGHT);
105984             if (leftLoc === rightLoc) {
105985               return false
105986             }
105987             if (rightLoc !== currLoc) {
105988               return false
105989             }
105990             currLoc = leftLoc;
105991           }
105992           return true
105993         };
105994         EdgeEndStar.prototype.findIndex = function findIndex (eSearch) {
105995             var this$1 = this;
105996
105997           this.iterator();
105998           for (var i = 0; i < this._edgeList.size(); i++) {
105999             var e = this$1._edgeList.get(i);
106000             if (e === eSearch) { return i }
106001           }
106002           return -1
106003         };
106004         EdgeEndStar.prototype.iterator = function iterator () {
106005           return this.getEdges().iterator()
106006         };
106007         EdgeEndStar.prototype.getEdges = function getEdges () {
106008           if (this._edgeList === null) {
106009             this._edgeList = new ArrayList(this._edgeMap.values());
106010           }
106011           return this._edgeList
106012         };
106013         EdgeEndStar.prototype.getLocation = function getLocation (geomIndex, p, geom) {
106014           if (this._ptInAreaLocation[geomIndex] === Location.NONE) {
106015             this._ptInAreaLocation[geomIndex] = SimplePointInAreaLocator.locate(p, geom[geomIndex].getGeometry());
106016           }
106017           return this._ptInAreaLocation[geomIndex]
106018         };
106019         EdgeEndStar.prototype.toString = function toString () {
106020           var buf = new StringBuffer();
106021           buf.append('EdgeEndStar:   ' + this.getCoordinate());
106022           buf.append('\n');
106023           for (var it = this.iterator(); it.hasNext();) {
106024             var e = it.next();
106025             buf.append(e);
106026             buf.append('\n');
106027           }
106028           return buf.toString()
106029         };
106030         EdgeEndStar.prototype.computeEdgeEndLabels = function computeEdgeEndLabels (boundaryNodeRule) {
106031           for (var it = this.iterator(); it.hasNext();) {
106032             var ee = it.next();
106033             ee.computeLabel(boundaryNodeRule);
106034           }
106035         };
106036         EdgeEndStar.prototype.computeLabelling = function computeLabelling (geomGraph) {
106037             var this$1 = this;
106038
106039           this.computeEdgeEndLabels(geomGraph[0].getBoundaryNodeRule());
106040           this.propagateSideLabels(0);
106041           this.propagateSideLabels(1);
106042           var hasDimensionalCollapseEdge = [false, false];
106043           for (var it = this.iterator(); it.hasNext();) {
106044             var e = it.next();
106045             var label = e.getLabel();
106046             for (var geomi = 0; geomi < 2; geomi++) {
106047               if (label.isLine(geomi) && label.getLocation(geomi) === Location.BOUNDARY) { hasDimensionalCollapseEdge[geomi] = true; }
106048             }
106049           }
106050           for (var it$1 = this.iterator(); it$1.hasNext();) {
106051             var e$1 = it$1.next();
106052             var label$1 = e$1.getLabel();
106053             for (var geomi$1 = 0; geomi$1 < 2; geomi$1++) {
106054               if (label$1.isAnyNull(geomi$1)) {
106055                 var loc = Location.NONE;
106056                 if (hasDimensionalCollapseEdge[geomi$1]) {
106057                   loc = Location.EXTERIOR;
106058                 } else {
106059                   var p = e$1.getCoordinate();
106060                   loc = this$1.getLocation(geomi$1, p, geomGraph);
106061                 }
106062                 label$1.setAllLocationsIfNull(geomi$1, loc);
106063               }
106064             }
106065           }
106066         };
106067         EdgeEndStar.prototype.getDegree = function getDegree () {
106068           return this._edgeMap.size()
106069         };
106070         EdgeEndStar.prototype.insertEdgeEnd = function insertEdgeEnd (e, obj) {
106071           this._edgeMap.put(e, obj);
106072           this._edgeList = null;
106073         };
106074         EdgeEndStar.prototype.interfaces_ = function interfaces_ () {
106075           return []
106076         };
106077         EdgeEndStar.prototype.getClass = function getClass () {
106078           return EdgeEndStar
106079         };
106080
106081         var DirectedEdgeStar = (function (EdgeEndStar$$1) {
106082           function DirectedEdgeStar () {
106083             EdgeEndStar$$1.call(this);
106084             this._resultAreaEdgeList = null;
106085             this._label = null;
106086             this._SCANNING_FOR_INCOMING = 1;
106087             this._LINKING_TO_OUTGOING = 2;
106088           }
106089
106090           if ( EdgeEndStar$$1 ) DirectedEdgeStar.__proto__ = EdgeEndStar$$1;
106091           DirectedEdgeStar.prototype = Object.create( EdgeEndStar$$1 && EdgeEndStar$$1.prototype );
106092           DirectedEdgeStar.prototype.constructor = DirectedEdgeStar;
106093           DirectedEdgeStar.prototype.linkResultDirectedEdges = function linkResultDirectedEdges () {
106094             var this$1 = this;
106095
106096             this.getResultAreaEdges();
106097             var firstOut = null;
106098             var incoming = null;
106099             var state = this._SCANNING_FOR_INCOMING;
106100             for (var i = 0; i < this._resultAreaEdgeList.size(); i++) {
106101               var nextOut = this$1._resultAreaEdgeList.get(i);
106102               var nextIn = nextOut.getSym();
106103               if (!nextOut.getLabel().isArea()) { continue }
106104               if (firstOut === null && nextOut.isInResult()) { firstOut = nextOut; }
106105               switch (state) {
106106                 case this$1._SCANNING_FOR_INCOMING:
106107                   if (!nextIn.isInResult()) { continue }
106108                   incoming = nextIn;
106109                   state = this$1._LINKING_TO_OUTGOING;
106110                   break
106111                 case this$1._LINKING_TO_OUTGOING:
106112                   if (!nextOut.isInResult()) { continue }
106113                   incoming.setNext(nextOut);
106114                   state = this$1._SCANNING_FOR_INCOMING;
106115                   break
106116               }
106117             }
106118             if (state === this._LINKING_TO_OUTGOING) {
106119               if (firstOut === null) { throw new TopologyException('no outgoing dirEdge found', this.getCoordinate()) }
106120               Assert.isTrue(firstOut.isInResult(), 'unable to link last incoming dirEdge');
106121               incoming.setNext(firstOut);
106122             }
106123           };
106124           DirectedEdgeStar.prototype.insert = function insert (ee) {
106125             var de = ee;
106126             this.insertEdgeEnd(de, de);
106127           };
106128           DirectedEdgeStar.prototype.getRightmostEdge = function getRightmostEdge () {
106129             var edges = this.getEdges();
106130             var size = edges.size();
106131             if (size < 1) { return null }
106132             var de0 = edges.get(0);
106133             if (size === 1) { return de0 }
106134             var deLast = edges.get(size - 1);
106135             var quad0 = de0.getQuadrant();
106136             var quad1 = deLast.getQuadrant();
106137             if (Quadrant.isNorthern(quad0) && Quadrant.isNorthern(quad1)) { return de0; } else if (!Quadrant.isNorthern(quad0) && !Quadrant.isNorthern(quad1)) { return deLast; } else {
106138               // const nonHorizontalEdge = null
106139               if (de0.getDy() !== 0) { return de0; } else if (deLast.getDy() !== 0) { return deLast }
106140             }
106141             Assert.shouldNeverReachHere('found two horizontal edges incident on node');
106142             return null
106143           };
106144           DirectedEdgeStar.prototype.print = function print (out) {
106145             System.out.println('DirectedEdgeStar: ' + this.getCoordinate());
106146             for (var it = this.iterator(); it.hasNext();) {
106147               var de = it.next();
106148               out.print('out ');
106149               de.print(out);
106150               out.println();
106151               out.print('in ');
106152               de.getSym().print(out);
106153               out.println();
106154             }
106155           };
106156           DirectedEdgeStar.prototype.getResultAreaEdges = function getResultAreaEdges () {
106157             var this$1 = this;
106158
106159             if (this._resultAreaEdgeList !== null) { return this._resultAreaEdgeList }
106160             this._resultAreaEdgeList = new ArrayList();
106161             for (var it = this.iterator(); it.hasNext();) {
106162               var de = it.next();
106163               if (de.isInResult() || de.getSym().isInResult()) { this$1._resultAreaEdgeList.add(de); }
106164             }
106165             return this._resultAreaEdgeList
106166           };
106167           DirectedEdgeStar.prototype.updateLabelling = function updateLabelling (nodeLabel) {
106168             for (var it = this.iterator(); it.hasNext();) {
106169               var de = it.next();
106170               var label = de.getLabel();
106171               label.setAllLocationsIfNull(0, nodeLabel.getLocation(0));
106172               label.setAllLocationsIfNull(1, nodeLabel.getLocation(1));
106173             }
106174           };
106175           DirectedEdgeStar.prototype.linkAllDirectedEdges = function linkAllDirectedEdges () {
106176             var this$1 = this;
106177
106178             this.getEdges();
106179             var prevOut = null;
106180             var firstIn = null;
106181             for (var i = this._edgeList.size() - 1; i >= 0; i--) {
106182               var nextOut = this$1._edgeList.get(i);
106183               var nextIn = nextOut.getSym();
106184               if (firstIn === null) { firstIn = nextIn; }
106185               if (prevOut !== null) { nextIn.setNext(prevOut); }
106186               prevOut = nextOut;
106187             }
106188             firstIn.setNext(prevOut);
106189           };
106190           DirectedEdgeStar.prototype.computeDepths = function computeDepths () {
106191             var this$1 = this;
106192
106193             if (arguments.length === 1) {
106194               var de = arguments[0];
106195               var edgeIndex = this.findIndex(de);
106196               // const label = de.getLabel()
106197               var startDepth = de.getDepth(Position.LEFT);
106198               var targetLastDepth = de.getDepth(Position.RIGHT);
106199               var nextDepth = this.computeDepths(edgeIndex + 1, this._edgeList.size(), startDepth);
106200               var lastDepth = this.computeDepths(0, edgeIndex, nextDepth);
106201               if (lastDepth !== targetLastDepth) { throw new TopologyException('depth mismatch at ' + de.getCoordinate()) }
106202             } else if (arguments.length === 3) {
106203               var startIndex = arguments[0];
106204               var endIndex = arguments[1];
106205               var startDepth$1 = arguments[2];
106206               var currDepth = startDepth$1;
106207               for (var i = startIndex; i < endIndex; i++) {
106208                 var nextDe = this$1._edgeList.get(i);
106209                 // const label = nextDe.getLabel()
106210                 nextDe.setEdgeDepths(Position.RIGHT, currDepth);
106211                 currDepth = nextDe.getDepth(Position.LEFT);
106212               }
106213               return currDepth
106214             }
106215           };
106216           DirectedEdgeStar.prototype.mergeSymLabels = function mergeSymLabels () {
106217             for (var it = this.iterator(); it.hasNext();) {
106218               var de = it.next();
106219               var label = de.getLabel();
106220               label.merge(de.getSym().getLabel());
106221             }
106222           };
106223           DirectedEdgeStar.prototype.linkMinimalDirectedEdges = function linkMinimalDirectedEdges (er) {
106224             var this$1 = this;
106225
106226             var firstOut = null;
106227             var incoming = null;
106228             var state = this._SCANNING_FOR_INCOMING;
106229             for (var i = this._resultAreaEdgeList.size() - 1; i >= 0; i--) {
106230               var nextOut = this$1._resultAreaEdgeList.get(i);
106231               var nextIn = nextOut.getSym();
106232               if (firstOut === null && nextOut.getEdgeRing() === er) { firstOut = nextOut; }
106233               switch (state) {
106234                 case this$1._SCANNING_FOR_INCOMING:
106235                   if (nextIn.getEdgeRing() !== er) { continue }
106236                   incoming = nextIn;
106237                   state = this$1._LINKING_TO_OUTGOING;
106238                   break
106239                 case this$1._LINKING_TO_OUTGOING:
106240                   if (nextOut.getEdgeRing() !== er) { continue }
106241                   incoming.setNextMin(nextOut);
106242                   state = this$1._SCANNING_FOR_INCOMING;
106243                   break
106244               }
106245             }
106246             if (state === this._LINKING_TO_OUTGOING) {
106247               Assert.isTrue(firstOut !== null, 'found null for first outgoing dirEdge');
106248               Assert.isTrue(firstOut.getEdgeRing() === er, 'unable to link last incoming dirEdge');
106249               incoming.setNextMin(firstOut);
106250             }
106251           };
106252           DirectedEdgeStar.prototype.getOutgoingDegree = function getOutgoingDegree () {
106253             if (arguments.length === 0) {
106254               var degree = 0;
106255               for (var it = this.iterator(); it.hasNext();) {
106256                 var de = it.next();
106257                 if (de.isInResult()) { degree++; }
106258               }
106259               return degree
106260             } else if (arguments.length === 1) {
106261               var er = arguments[0];
106262               var degree$1 = 0;
106263               for (var it$1 = this.iterator(); it$1.hasNext();) {
106264                 var de$1 = it$1.next();
106265                 if (de$1.getEdgeRing() === er) { degree$1++; }
106266               }
106267               return degree$1
106268             }
106269           };
106270           DirectedEdgeStar.prototype.getLabel = function getLabel () {
106271             return this._label
106272           };
106273           DirectedEdgeStar.prototype.findCoveredLineEdges = function findCoveredLineEdges () {
106274             var startLoc = Location.NONE;
106275             for (var it = this.iterator(); it.hasNext();) {
106276               var nextOut = it.next();
106277               var nextIn = nextOut.getSym();
106278               if (!nextOut.isLineEdge()) {
106279                 if (nextOut.isInResult()) {
106280                   startLoc = Location.INTERIOR;
106281                   break
106282                 }
106283                 if (nextIn.isInResult()) {
106284                   startLoc = Location.EXTERIOR;
106285                   break
106286                 }
106287               }
106288             }
106289             if (startLoc === Location.NONE) { return null }
106290             var currLoc = startLoc;
106291             for (var it$1 = this.iterator(); it$1.hasNext();) {
106292               var nextOut$1 = it$1.next();
106293               var nextIn$1 = nextOut$1.getSym();
106294               if (nextOut$1.isLineEdge()) {
106295                 nextOut$1.getEdge().setCovered(currLoc === Location.INTERIOR);
106296               } else {
106297                 if (nextOut$1.isInResult()) { currLoc = Location.EXTERIOR; }
106298                 if (nextIn$1.isInResult()) { currLoc = Location.INTERIOR; }
106299               }
106300             }
106301           };
106302           DirectedEdgeStar.prototype.computeLabelling = function computeLabelling (geom) {
106303             var this$1 = this;
106304
106305             EdgeEndStar$$1.prototype.computeLabelling.call(this, geom);
106306             this._label = new Label(Location.NONE);
106307             for (var it = this.iterator(); it.hasNext();) {
106308               var ee = it.next();
106309               var e = ee.getEdge();
106310               var eLabel = e.getLabel();
106311               for (var i = 0; i < 2; i++) {
106312                 var eLoc = eLabel.getLocation(i);
106313                 if (eLoc === Location.INTERIOR || eLoc === Location.BOUNDARY) { this$1._label.setLocation(i, Location.INTERIOR); }
106314               }
106315             }
106316           };
106317           DirectedEdgeStar.prototype.interfaces_ = function interfaces_ () {
106318             return []
106319           };
106320           DirectedEdgeStar.prototype.getClass = function getClass () {
106321             return DirectedEdgeStar
106322           };
106323
106324           return DirectedEdgeStar;
106325         }(EdgeEndStar));
106326
106327         var OverlayNodeFactory = (function (NodeFactory$$1) {
106328           function OverlayNodeFactory () {
106329             NodeFactory$$1.apply(this, arguments);
106330           }
106331
106332           if ( NodeFactory$$1 ) OverlayNodeFactory.__proto__ = NodeFactory$$1;
106333           OverlayNodeFactory.prototype = Object.create( NodeFactory$$1 && NodeFactory$$1.prototype );
106334           OverlayNodeFactory.prototype.constructor = OverlayNodeFactory;
106335
106336           OverlayNodeFactory.prototype.createNode = function createNode (coord) {
106337             return new Node$1(coord, new DirectedEdgeStar())
106338           };
106339           OverlayNodeFactory.prototype.interfaces_ = function interfaces_ () {
106340             return []
106341           };
106342           OverlayNodeFactory.prototype.getClass = function getClass () {
106343             return OverlayNodeFactory
106344           };
106345
106346           return OverlayNodeFactory;
106347         }(NodeFactory));
106348
106349         var OrientedCoordinateArray = function OrientedCoordinateArray () {
106350           this._pts = null;
106351           this._orientation = null;
106352           var pts = arguments[0];
106353           this._pts = pts;
106354           this._orientation = OrientedCoordinateArray.orientation(pts);
106355         };
106356         OrientedCoordinateArray.prototype.compareTo = function compareTo (o1) {
106357           var oca = o1;
106358           var comp = OrientedCoordinateArray.compareOriented(this._pts, this._orientation, oca._pts, oca._orientation);
106359           return comp
106360         };
106361         OrientedCoordinateArray.prototype.interfaces_ = function interfaces_ () {
106362           return [Comparable]
106363         };
106364         OrientedCoordinateArray.prototype.getClass = function getClass () {
106365           return OrientedCoordinateArray
106366         };
106367         OrientedCoordinateArray.orientation = function orientation (pts) {
106368           return CoordinateArrays.increasingDirection(pts) === 1
106369         };
106370         OrientedCoordinateArray.compareOriented = function compareOriented (pts1, orientation1, pts2, orientation2) {
106371           var dir1 = orientation1 ? 1 : -1;
106372           var dir2 = orientation2 ? 1 : -1;
106373           var limit1 = orientation1 ? pts1.length : -1;
106374           var limit2 = orientation2 ? pts2.length : -1;
106375           var i1 = orientation1 ? 0 : pts1.length - 1;
106376           var i2 = orientation2 ? 0 : pts2.length - 1;
106377           // const comp = 0
106378           while (true) {
106379             var compPt = pts1[i1].compareTo(pts2[i2]);
106380             if (compPt !== 0) { return compPt }
106381             i1 += dir1;
106382             i2 += dir2;
106383             var done1 = i1 === limit1;
106384             var done2 = i2 === limit2;
106385             if (done1 && !done2) { return -1 }
106386             if (!done1 && done2) { return 1 }
106387             if (done1 && done2) { return 0 }
106388           }
106389         };
106390
106391         var EdgeList = function EdgeList () {
106392           this._edges = new ArrayList();
106393           this._ocaMap = new TreeMap();
106394         };
106395         EdgeList.prototype.print = function print (out) {
106396             var this$1 = this;
106397
106398           out.print('MULTILINESTRING ( ');
106399           for (var j = 0; j < this._edges.size(); j++) {
106400             var e = this$1._edges.get(j);
106401             if (j > 0) { out.print(','); }
106402             out.print('(');
106403             var pts = e.getCoordinates();
106404             for (var i = 0; i < pts.length; i++) {
106405               if (i > 0) { out.print(','); }
106406               out.print(pts[i].x + ' ' + pts[i].y);
106407             }
106408             out.println(')');
106409           }
106410           out.print(')  ');
106411         };
106412         EdgeList.prototype.addAll = function addAll (edgeColl) {
106413             var this$1 = this;
106414
106415           for (var i = edgeColl.iterator(); i.hasNext();) {
106416             this$1.add(i.next());
106417           }
106418         };
106419         EdgeList.prototype.findEdgeIndex = function findEdgeIndex (e) {
106420             var this$1 = this;
106421
106422           for (var i = 0; i < this._edges.size(); i++) {
106423             if (this$1._edges.get(i).equals(e)) { return i }
106424           }
106425           return -1
106426         };
106427         EdgeList.prototype.iterator = function iterator () {
106428           return this._edges.iterator()
106429         };
106430         EdgeList.prototype.getEdges = function getEdges () {
106431           return this._edges
106432         };
106433         EdgeList.prototype.get = function get (i) {
106434           return this._edges.get(i)
106435         };
106436         EdgeList.prototype.findEqualEdge = function findEqualEdge (e) {
106437           var oca = new OrientedCoordinateArray(e.getCoordinates());
106438           var matchEdge = this._ocaMap.get(oca);
106439           return matchEdge
106440         };
106441         EdgeList.prototype.add = function add (e) {
106442           this._edges.add(e);
106443           var oca = new OrientedCoordinateArray(e.getCoordinates());
106444           this._ocaMap.put(oca, e);
106445         };
106446         EdgeList.prototype.interfaces_ = function interfaces_ () {
106447           return []
106448         };
106449         EdgeList.prototype.getClass = function getClass () {
106450           return EdgeList
106451         };
106452
106453         var SegmentIntersector = function SegmentIntersector () {};
106454
106455         SegmentIntersector.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {};
106456         SegmentIntersector.prototype.isDone = function isDone () {};
106457         SegmentIntersector.prototype.interfaces_ = function interfaces_ () {
106458           return []
106459         };
106460         SegmentIntersector.prototype.getClass = function getClass () {
106461           return SegmentIntersector
106462         };
106463
106464         var IntersectionAdder = function IntersectionAdder () {
106465           this._hasIntersection = false;
106466           this._hasProper = false;
106467           this._hasProperInterior = false;
106468           this._hasInterior = false;
106469           this._properIntersectionPoint = null;
106470           this._li = null;
106471           this._isSelfIntersection = null;
106472           this.numIntersections = 0;
106473           this.numInteriorIntersections = 0;
106474           this.numProperIntersections = 0;
106475           this.numTests = 0;
106476           var li = arguments[0];
106477           this._li = li;
106478         };
106479         IntersectionAdder.prototype.isTrivialIntersection = function isTrivialIntersection (e0, segIndex0, e1, segIndex1) {
106480           if (e0 === e1) {
106481             if (this._li.getIntersectionNum() === 1) {
106482               if (IntersectionAdder.isAdjacentSegments(segIndex0, segIndex1)) { return true }
106483               if (e0.isClosed()) {
106484                 var maxSegIndex = e0.size() - 1;
106485                 if ((segIndex0 === 0 && segIndex1 === maxSegIndex) ||
106486                     (segIndex1 === 0 && segIndex0 === maxSegIndex)) {
106487                   return true
106488                 }
106489               }
106490             }
106491           }
106492           return false
106493         };
106494         IntersectionAdder.prototype.getProperIntersectionPoint = function getProperIntersectionPoint () {
106495           return this._properIntersectionPoint
106496         };
106497         IntersectionAdder.prototype.hasProperInteriorIntersection = function hasProperInteriorIntersection () {
106498           return this._hasProperInterior
106499         };
106500         IntersectionAdder.prototype.getLineIntersector = function getLineIntersector () {
106501           return this._li
106502         };
106503         IntersectionAdder.prototype.hasProperIntersection = function hasProperIntersection () {
106504           return this._hasProper
106505         };
106506         IntersectionAdder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
106507           if (e0 === e1 && segIndex0 === segIndex1) { return null }
106508           this.numTests++;
106509           var p00 = e0.getCoordinates()[segIndex0];
106510           var p01 = e0.getCoordinates()[segIndex0 + 1];
106511           var p10 = e1.getCoordinates()[segIndex1];
106512           var p11 = e1.getCoordinates()[segIndex1 + 1];
106513           this._li.computeIntersection(p00, p01, p10, p11);
106514           if (this._li.hasIntersection()) {
106515             this.numIntersections++;
106516             if (this._li.isInteriorIntersection()) {
106517               this.numInteriorIntersections++;
106518               this._hasInterior = true;
106519             }
106520             if (!this.isTrivialIntersection(e0, segIndex0, e1, segIndex1)) {
106521               this._hasIntersection = true;
106522               e0.addIntersections(this._li, segIndex0, 0);
106523               e1.addIntersections(this._li, segIndex1, 1);
106524               if (this._li.isProper()) {
106525                 this.numProperIntersections++;
106526                 this._hasProper = true;
106527                 this._hasProperInterior = true;
106528               }
106529             }
106530           }
106531         };
106532         IntersectionAdder.prototype.hasIntersection = function hasIntersection () {
106533           return this._hasIntersection
106534         };
106535         IntersectionAdder.prototype.isDone = function isDone () {
106536           return false
106537         };
106538         IntersectionAdder.prototype.hasInteriorIntersection = function hasInteriorIntersection () {
106539           return this._hasInterior
106540         };
106541         IntersectionAdder.prototype.interfaces_ = function interfaces_ () {
106542           return [SegmentIntersector]
106543         };
106544         IntersectionAdder.prototype.getClass = function getClass () {
106545           return IntersectionAdder
106546         };
106547         IntersectionAdder.isAdjacentSegments = function isAdjacentSegments (i1, i2) {
106548           return Math.abs(i1 - i2) === 1
106549         };
106550
106551         var EdgeIntersection = function EdgeIntersection () {
106552           this.coord = null;
106553           this.segmentIndex = null;
106554           this.dist = null;
106555           var coord = arguments[0];
106556           var segmentIndex = arguments[1];
106557           var dist = arguments[2];
106558           this.coord = new Coordinate(coord);
106559           this.segmentIndex = segmentIndex;
106560           this.dist = dist;
106561         };
106562         EdgeIntersection.prototype.getSegmentIndex = function getSegmentIndex () {
106563           return this.segmentIndex
106564         };
106565         EdgeIntersection.prototype.getCoordinate = function getCoordinate () {
106566           return this.coord
106567         };
106568         EdgeIntersection.prototype.print = function print (out) {
106569           out.print(this.coord);
106570           out.print(' seg # = ' + this.segmentIndex);
106571           out.println(' dist = ' + this.dist);
106572         };
106573         EdgeIntersection.prototype.compareTo = function compareTo (obj) {
106574           var other = obj;
106575           return this.compare(other.segmentIndex, other.dist)
106576         };
106577         EdgeIntersection.prototype.isEndPoint = function isEndPoint (maxSegmentIndex) {
106578           if (this.segmentIndex === 0 && this.dist === 0.0) { return true }
106579           if (this.segmentIndex === maxSegmentIndex) { return true }
106580           return false
106581         };
106582         EdgeIntersection.prototype.toString = function toString () {
106583           return this.coord + ' seg # = ' + this.segmentIndex + ' dist = ' + this.dist
106584         };
106585         EdgeIntersection.prototype.getDistance = function getDistance () {
106586           return this.dist
106587         };
106588         EdgeIntersection.prototype.compare = function compare (segmentIndex, dist) {
106589           if (this.segmentIndex < segmentIndex) { return -1 }
106590           if (this.segmentIndex > segmentIndex) { return 1 }
106591           if (this.dist < dist) { return -1 }
106592           if (this.dist > dist) { return 1 }
106593           return 0
106594         };
106595         EdgeIntersection.prototype.interfaces_ = function interfaces_ () {
106596           return [Comparable]
106597         };
106598         EdgeIntersection.prototype.getClass = function getClass () {
106599           return EdgeIntersection
106600         };
106601
106602         var EdgeIntersectionList = function EdgeIntersectionList () {
106603           this._nodeMap = new TreeMap();
106604           this.edge = null;
106605           var edge = arguments[0];
106606           this.edge = edge;
106607         };
106608         EdgeIntersectionList.prototype.print = function print (out) {
106609           out.println('Intersections:');
106610           for (var it = this.iterator(); it.hasNext();) {
106611             var ei = it.next();
106612             ei.print(out);
106613           }
106614         };
106615         EdgeIntersectionList.prototype.iterator = function iterator () {
106616           return this._nodeMap.values().iterator()
106617         };
106618         EdgeIntersectionList.prototype.addSplitEdges = function addSplitEdges (edgeList) {
106619             var this$1 = this;
106620
106621           this.addEndpoints();
106622           var it = this.iterator();
106623           var eiPrev = it.next();
106624           while (it.hasNext()) {
106625             var ei = it.next();
106626             var newEdge = this$1.createSplitEdge(eiPrev, ei);
106627             edgeList.add(newEdge);
106628             eiPrev = ei;
106629           }
106630         };
106631         EdgeIntersectionList.prototype.addEndpoints = function addEndpoints () {
106632           var maxSegIndex = this.edge.pts.length - 1;
106633           this.add(this.edge.pts[0], 0, 0.0);
106634           this.add(this.edge.pts[maxSegIndex], maxSegIndex, 0.0);
106635         };
106636         EdgeIntersectionList.prototype.createSplitEdge = function createSplitEdge (ei0, ei1) {
106637             var this$1 = this;
106638
106639           var npts = ei1.segmentIndex - ei0.segmentIndex + 2;
106640           var lastSegStartPt = this.edge.pts[ei1.segmentIndex];
106641           var useIntPt1 = ei1.dist > 0.0 || !ei1.coord.equals2D(lastSegStartPt);
106642           if (!useIntPt1) {
106643             npts--;
106644           }
106645           var pts = new Array(npts).fill(null);
106646           var ipt = 0;
106647           pts[ipt++] = new Coordinate(ei0.coord);
106648           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
106649             pts[ipt++] = this$1.edge.pts[i];
106650           }
106651           if (useIntPt1) { pts[ipt] = ei1.coord; }
106652           return new Edge(pts, new Label(this.edge._label))
106653         };
106654         EdgeIntersectionList.prototype.add = function add (intPt, segmentIndex, dist) {
106655           var eiNew = new EdgeIntersection(intPt, segmentIndex, dist);
106656           var ei = this._nodeMap.get(eiNew);
106657           if (ei !== null) {
106658             return ei
106659           }
106660           this._nodeMap.put(eiNew, eiNew);
106661           return eiNew
106662         };
106663         EdgeIntersectionList.prototype.isIntersection = function isIntersection (pt) {
106664           for (var it = this.iterator(); it.hasNext();) {
106665             var ei = it.next();
106666             if (ei.coord.equals(pt)) { return true }
106667           }
106668           return false
106669         };
106670         EdgeIntersectionList.prototype.interfaces_ = function interfaces_ () {
106671           return []
106672         };
106673         EdgeIntersectionList.prototype.getClass = function getClass () {
106674           return EdgeIntersectionList
106675         };
106676
106677         var MonotoneChainIndexer = function MonotoneChainIndexer () {};
106678
106679         MonotoneChainIndexer.prototype.getChainStartIndices = function getChainStartIndices (pts) {
106680             var this$1 = this;
106681
106682           var start = 0;
106683           var startIndexList = new ArrayList();
106684           startIndexList.add(new Integer(start));
106685           do {
106686             var last = this$1.findChainEnd(pts, start);
106687             startIndexList.add(new Integer(last));
106688             start = last;
106689           } while (start < pts.length - 1)
106690           var startIndex = MonotoneChainIndexer.toIntArray(startIndexList);
106691           return startIndex
106692         };
106693         MonotoneChainIndexer.prototype.findChainEnd = function findChainEnd (pts, start) {
106694           var chainQuad = Quadrant.quadrant(pts[start], pts[start + 1]);
106695           var last = start + 1;
106696           while (last < pts.length) {
106697             var quad = Quadrant.quadrant(pts[last - 1], pts[last]);
106698             if (quad !== chainQuad) { break }
106699             last++;
106700           }
106701           return last - 1
106702         };
106703         MonotoneChainIndexer.prototype.interfaces_ = function interfaces_ () {
106704           return []
106705         };
106706         MonotoneChainIndexer.prototype.getClass = function getClass () {
106707           return MonotoneChainIndexer
106708         };
106709         MonotoneChainIndexer.toIntArray = function toIntArray (list) {
106710           var array = new Array(list.size()).fill(null);
106711           for (var i = 0; i < array.length; i++) {
106712             array[i] = list.get(i).intValue();
106713           }
106714           return array
106715         };
106716
106717         var MonotoneChainEdge = function MonotoneChainEdge () {
106718           this.e = null;
106719           this.pts = null;
106720           this.startIndex = null;
106721           this.env1 = new Envelope();
106722           this.env2 = new Envelope();
106723           var e = arguments[0];
106724           this.e = e;
106725           this.pts = e.getCoordinates();
106726           var mcb = new MonotoneChainIndexer();
106727           this.startIndex = mcb.getChainStartIndices(this.pts);
106728         };
106729         MonotoneChainEdge.prototype.getCoordinates = function getCoordinates () {
106730           return this.pts
106731         };
106732         MonotoneChainEdge.prototype.getMaxX = function getMaxX (chainIndex) {
106733           var x1 = this.pts[this.startIndex[chainIndex]].x;
106734           var x2 = this.pts[this.startIndex[chainIndex + 1]].x;
106735           return x1 > x2 ? x1 : x2
106736         };
106737         MonotoneChainEdge.prototype.getMinX = function getMinX (chainIndex) {
106738           var x1 = this.pts[this.startIndex[chainIndex]].x;
106739           var x2 = this.pts[this.startIndex[chainIndex + 1]].x;
106740           return x1 < x2 ? x1 : x2
106741         };
106742         MonotoneChainEdge.prototype.computeIntersectsForChain = function computeIntersectsForChain () {
106743           if (arguments.length === 4) {
106744             var chainIndex0 = arguments[0];
106745             var mce = arguments[1];
106746             var chainIndex1 = arguments[2];
106747             var si = arguments[3];
106748             this.computeIntersectsForChain(this.startIndex[chainIndex0], this.startIndex[chainIndex0 + 1], mce, mce.startIndex[chainIndex1], mce.startIndex[chainIndex1 + 1], si);
106749           } else if (arguments.length === 6) {
106750             var start0 = arguments[0];
106751             var end0 = arguments[1];
106752             var mce$1 = arguments[2];
106753             var start1 = arguments[3];
106754             var end1 = arguments[4];
106755             var ei = arguments[5];
106756             var p00 = this.pts[start0];
106757             var p01 = this.pts[end0];
106758             var p10 = mce$1.pts[start1];
106759             var p11 = mce$1.pts[end1];
106760             if (end0 - start0 === 1 && end1 - start1 === 1) {
106761               ei.addIntersections(this.e, start0, mce$1.e, start1);
106762               return null
106763             }
106764             this.env1.init(p00, p01);
106765             this.env2.init(p10, p11);
106766             if (!this.env1.intersects(this.env2)) { return null }
106767             var mid0 = Math.trunc((start0 + end0) / 2);
106768             var mid1 = Math.trunc((start1 + end1) / 2);
106769             if (start0 < mid0) {
106770               if (start1 < mid1) { this.computeIntersectsForChain(start0, mid0, mce$1, start1, mid1, ei); }
106771               if (mid1 < end1) { this.computeIntersectsForChain(start0, mid0, mce$1, mid1, end1, ei); }
106772             }
106773             if (mid0 < end0) {
106774               if (start1 < mid1) { this.computeIntersectsForChain(mid0, end0, mce$1, start1, mid1, ei); }
106775               if (mid1 < end1) { this.computeIntersectsForChain(mid0, end0, mce$1, mid1, end1, ei); }
106776             }
106777           }
106778         };
106779         MonotoneChainEdge.prototype.getStartIndexes = function getStartIndexes () {
106780           return this.startIndex
106781         };
106782         MonotoneChainEdge.prototype.computeIntersects = function computeIntersects (mce, si) {
106783             var this$1 = this;
106784
106785           for (var i = 0; i < this.startIndex.length - 1; i++) {
106786             for (var j = 0; j < mce.startIndex.length - 1; j++) {
106787               this$1.computeIntersectsForChain(i, mce, j, si);
106788             }
106789           }
106790         };
106791         MonotoneChainEdge.prototype.interfaces_ = function interfaces_ () {
106792           return []
106793         };
106794         MonotoneChainEdge.prototype.getClass = function getClass () {
106795           return MonotoneChainEdge
106796         };
106797
106798         var Depth = function Depth () {
106799           var this$1 = this;
106800
106801           this._depth = Array(2).fill().map(function () { return Array(3); });
106802           for (var i = 0; i < 2; i++) {
106803             for (var j = 0; j < 3; j++) {
106804               this$1._depth[i][j] = Depth.NULL_VALUE;
106805             }
106806           }
106807         };
106808
106809         var staticAccessors$31 = { NULL_VALUE: { configurable: true } };
106810         Depth.prototype.getDepth = function getDepth (geomIndex, posIndex) {
106811           return this._depth[geomIndex][posIndex]
106812         };
106813         Depth.prototype.setDepth = function setDepth (geomIndex, posIndex, depthValue) {
106814           this._depth[geomIndex][posIndex] = depthValue;
106815         };
106816         Depth.prototype.isNull = function isNull () {
106817             var this$1 = this;
106818
106819           if (arguments.length === 0) {
106820             for (var i = 0; i < 2; i++) {
106821               for (var j = 0; j < 3; j++) {
106822                 if (this$1._depth[i][j] !== Depth.NULL_VALUE) { return false }
106823               }
106824             }
106825             return true
106826           } else if (arguments.length === 1) {
106827             var geomIndex = arguments[0];
106828             return this._depth[geomIndex][1] === Depth.NULL_VALUE
106829           } else if (arguments.length === 2) {
106830             var geomIndex$1 = arguments[0];
106831             var posIndex = arguments[1];
106832             return this._depth[geomIndex$1][posIndex] === Depth.NULL_VALUE
106833           }
106834         };
106835         Depth.prototype.normalize = function normalize () {
106836             var this$1 = this;
106837
106838           for (var i = 0; i < 2; i++) {
106839             if (!this$1.isNull(i)) {
106840               var minDepth = this$1._depth[i][1];
106841               if (this$1._depth[i][2] < minDepth) { minDepth = this$1._depth[i][2]; }
106842               if (minDepth < 0) { minDepth = 0; }
106843               for (var j = 1; j < 3; j++) {
106844                 var newValue = 0;
106845                 if (this$1._depth[i][j] > minDepth) { newValue = 1; }
106846                 this$1._depth[i][j] = newValue;
106847               }
106848             }
106849           }
106850         };
106851         Depth.prototype.getDelta = function getDelta (geomIndex) {
106852           return this._depth[geomIndex][Position.RIGHT] - this._depth[geomIndex][Position.LEFT]
106853         };
106854         Depth.prototype.getLocation = function getLocation (geomIndex, posIndex) {
106855           if (this._depth[geomIndex][posIndex] <= 0) { return Location.EXTERIOR }
106856           return Location.INTERIOR
106857         };
106858         Depth.prototype.toString = function toString () {
106859           return 'A: ' + this._depth[0][1] + ',' + this._depth[0][2] + ' B: ' + this._depth[1][1] + ',' + this._depth[1][2]
106860         };
106861         Depth.prototype.add = function add () {
106862             var this$1 = this;
106863
106864           if (arguments.length === 1) {
106865             var lbl = arguments[0];
106866             for (var i = 0; i < 2; i++) {
106867               for (var j = 1; j < 3; j++) {
106868                 var loc = lbl.getLocation(i, j);
106869                 if (loc === Location.EXTERIOR || loc === Location.INTERIOR) {
106870                   if (this$1.isNull(i, j)) {
106871                     this$1._depth[i][j] = Depth.depthAtLocation(loc);
106872                   } else { this$1._depth[i][j] += Depth.depthAtLocation(loc); }
106873                 }
106874               }
106875             }
106876           } else if (arguments.length === 3) {
106877             var geomIndex = arguments[0];
106878             var posIndex = arguments[1];
106879             var location = arguments[2];
106880             if (location === Location.INTERIOR) { this._depth[geomIndex][posIndex]++; }
106881           }
106882         };
106883         Depth.prototype.interfaces_ = function interfaces_ () {
106884           return []
106885         };
106886         Depth.prototype.getClass = function getClass () {
106887           return Depth
106888         };
106889         Depth.depthAtLocation = function depthAtLocation (location) {
106890           if (location === Location.EXTERIOR) { return 0 }
106891           if (location === Location.INTERIOR) { return 1 }
106892           return Depth.NULL_VALUE
106893         };
106894         staticAccessors$31.NULL_VALUE.get = function () { return -1 };
106895
106896         Object.defineProperties( Depth, staticAccessors$31 );
106897
106898         var Edge = (function (GraphComponent$$1) {
106899           function Edge () {
106900             GraphComponent$$1.call(this);
106901             this.pts = null;
106902             this._env = null;
106903             this.eiList = new EdgeIntersectionList(this);
106904             this._name = null;
106905             this._mce = null;
106906             this._isIsolated = true;
106907             this._depth = new Depth();
106908             this._depthDelta = 0;
106909             if (arguments.length === 1) {
106910               var pts = arguments[0];
106911               Edge.call(this, pts, null);
106912             } else if (arguments.length === 2) {
106913               var pts$1 = arguments[0];
106914               var label = arguments[1];
106915               this.pts = pts$1;
106916               this._label = label;
106917             }
106918           }
106919
106920           if ( GraphComponent$$1 ) Edge.__proto__ = GraphComponent$$1;
106921           Edge.prototype = Object.create( GraphComponent$$1 && GraphComponent$$1.prototype );
106922           Edge.prototype.constructor = Edge;
106923           Edge.prototype.getDepth = function getDepth () {
106924             return this._depth
106925           };
106926           Edge.prototype.getCollapsedEdge = function getCollapsedEdge () {
106927             var newPts = new Array(2).fill(null);
106928             newPts[0] = this.pts[0];
106929             newPts[1] = this.pts[1];
106930             var newe = new Edge(newPts, Label.toLineLabel(this._label));
106931             return newe
106932           };
106933           Edge.prototype.isIsolated = function isIsolated () {
106934             return this._isIsolated
106935           };
106936           Edge.prototype.getCoordinates = function getCoordinates () {
106937             return this.pts
106938           };
106939           Edge.prototype.setIsolated = function setIsolated (isIsolated) {
106940             this._isIsolated = isIsolated;
106941           };
106942           Edge.prototype.setName = function setName (name) {
106943             this._name = name;
106944           };
106945           Edge.prototype.equals = function equals (o) {
106946             var this$1 = this;
106947
106948             if (!(o instanceof Edge)) { return false }
106949             var e = o;
106950             if (this.pts.length !== e.pts.length) { return false }
106951             var isEqualForward = true;
106952             var isEqualReverse = true;
106953             var iRev = this.pts.length;
106954             for (var i = 0; i < this.pts.length; i++) {
106955               if (!this$1.pts[i].equals2D(e.pts[i])) {
106956                 isEqualForward = false;
106957               }
106958               if (!this$1.pts[i].equals2D(e.pts[--iRev])) {
106959                 isEqualReverse = false;
106960               }
106961               if (!isEqualForward && !isEqualReverse) { return false }
106962             }
106963             return true
106964           };
106965           Edge.prototype.getCoordinate = function getCoordinate () {
106966             if (arguments.length === 0) {
106967               if (this.pts.length > 0) { return this.pts[0] }
106968               return null
106969             } else if (arguments.length === 1) {
106970               var i = arguments[0];
106971               return this.pts[i]
106972             }
106973           };
106974           Edge.prototype.print = function print (out) {
106975             var this$1 = this;
106976
106977             out.print('edge ' + this._name + ': ');
106978             out.print('LINESTRING (');
106979             for (var i = 0; i < this.pts.length; i++) {
106980               if (i > 0) { out.print(','); }
106981               out.print(this$1.pts[i].x + ' ' + this$1.pts[i].y);
106982             }
106983             out.print(')  ' + this._label + ' ' + this._depthDelta);
106984           };
106985           Edge.prototype.computeIM = function computeIM (im) {
106986             Edge.updateIM(this._label, im);
106987           };
106988           Edge.prototype.isCollapsed = function isCollapsed () {
106989             if (!this._label.isArea()) { return false }
106990             if (this.pts.length !== 3) { return false }
106991             if (this.pts[0].equals(this.pts[2])) { return true }
106992             return false
106993           };
106994           Edge.prototype.isClosed = function isClosed () {
106995             return this.pts[0].equals(this.pts[this.pts.length - 1])
106996           };
106997           Edge.prototype.getMaximumSegmentIndex = function getMaximumSegmentIndex () {
106998             return this.pts.length - 1
106999           };
107000           Edge.prototype.getDepthDelta = function getDepthDelta () {
107001             return this._depthDelta
107002           };
107003           Edge.prototype.getNumPoints = function getNumPoints () {
107004             return this.pts.length
107005           };
107006           Edge.prototype.printReverse = function printReverse (out) {
107007             var this$1 = this;
107008
107009             out.print('edge ' + this._name + ': ');
107010             for (var i = this.pts.length - 1; i >= 0; i--) {
107011               out.print(this$1.pts[i] + ' ');
107012             }
107013             out.println('');
107014           };
107015           Edge.prototype.getMonotoneChainEdge = function getMonotoneChainEdge () {
107016             if (this._mce === null) { this._mce = new MonotoneChainEdge(this); }
107017             return this._mce
107018           };
107019           Edge.prototype.getEnvelope = function getEnvelope () {
107020             var this$1 = this;
107021
107022             if (this._env === null) {
107023               this._env = new Envelope();
107024               for (var i = 0; i < this.pts.length; i++) {
107025                 this$1._env.expandToInclude(this$1.pts[i]);
107026               }
107027             }
107028             return this._env
107029           };
107030           Edge.prototype.addIntersection = function addIntersection (li, segmentIndex, geomIndex, intIndex) {
107031             var intPt = new Coordinate(li.getIntersection(intIndex));
107032             var normalizedSegmentIndex = segmentIndex;
107033             var dist = li.getEdgeDistance(geomIndex, intIndex);
107034             var nextSegIndex = normalizedSegmentIndex + 1;
107035             if (nextSegIndex < this.pts.length) {
107036               var nextPt = this.pts[nextSegIndex];
107037               if (intPt.equals2D(nextPt)) {
107038                 normalizedSegmentIndex = nextSegIndex;
107039                 dist = 0.0;
107040               }
107041             }
107042             this.eiList.add(intPt, normalizedSegmentIndex, dist);
107043           };
107044           Edge.prototype.toString = function toString () {
107045             var this$1 = this;
107046
107047             var buf = new StringBuffer();
107048             buf.append('edge ' + this._name + ': ');
107049             buf.append('LINESTRING (');
107050             for (var i = 0; i < this.pts.length; i++) {
107051               if (i > 0) { buf.append(','); }
107052               buf.append(this$1.pts[i].x + ' ' + this$1.pts[i].y);
107053             }
107054             buf.append(')  ' + this._label + ' ' + this._depthDelta);
107055             return buf.toString()
107056           };
107057           Edge.prototype.isPointwiseEqual = function isPointwiseEqual (e) {
107058             var this$1 = this;
107059
107060             if (this.pts.length !== e.pts.length) { return false }
107061             for (var i = 0; i < this.pts.length; i++) {
107062               if (!this$1.pts[i].equals2D(e.pts[i])) {
107063                 return false
107064               }
107065             }
107066             return true
107067           };
107068           Edge.prototype.setDepthDelta = function setDepthDelta (depthDelta) {
107069             this._depthDelta = depthDelta;
107070           };
107071           Edge.prototype.getEdgeIntersectionList = function getEdgeIntersectionList () {
107072             return this.eiList
107073           };
107074           Edge.prototype.addIntersections = function addIntersections (li, segmentIndex, geomIndex) {
107075             var this$1 = this;
107076
107077             for (var i = 0; i < li.getIntersectionNum(); i++) {
107078               this$1.addIntersection(li, segmentIndex, geomIndex, i);
107079             }
107080           };
107081           Edge.prototype.interfaces_ = function interfaces_ () {
107082             return []
107083           };
107084           Edge.prototype.getClass = function getClass () {
107085             return Edge
107086           };
107087           Edge.updateIM = function updateIM () {
107088             if (arguments.length === 2) {
107089               var label = arguments[0];
107090               var im = arguments[1];
107091               im.setAtLeastIfValid(label.getLocation(0, Position.ON), label.getLocation(1, Position.ON), 1);
107092               if (label.isArea()) {
107093                 im.setAtLeastIfValid(label.getLocation(0, Position.LEFT), label.getLocation(1, Position.LEFT), 2);
107094                 im.setAtLeastIfValid(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), 2);
107095               }
107096             } else { return GraphComponent$$1.prototype.updateIM.apply(this, arguments) }
107097           };
107098
107099           return Edge;
107100         }(GraphComponent));
107101
107102         var BufferBuilder = function BufferBuilder (bufParams) {
107103           this._workingPrecisionModel = null;
107104           this._workingNoder = null;
107105           this._geomFact = null;
107106           this._graph = null;
107107           this._edgeList = new EdgeList();
107108           this._bufParams = bufParams || null;
107109         };
107110         BufferBuilder.prototype.setWorkingPrecisionModel = function setWorkingPrecisionModel (pm) {
107111           this._workingPrecisionModel = pm;
107112         };
107113         BufferBuilder.prototype.insertUniqueEdge = function insertUniqueEdge (e) {
107114           var existingEdge = this._edgeList.findEqualEdge(e);
107115           if (existingEdge !== null) {
107116             var existingLabel = existingEdge.getLabel();
107117             var labelToMerge = e.getLabel();
107118             if (!existingEdge.isPointwiseEqual(e)) {
107119               labelToMerge = new Label(e.getLabel());
107120               labelToMerge.flip();
107121             }
107122             existingLabel.merge(labelToMerge);
107123             var mergeDelta = BufferBuilder.depthDelta(labelToMerge);
107124             var existingDelta = existingEdge.getDepthDelta();
107125             var newDelta = existingDelta + mergeDelta;
107126             existingEdge.setDepthDelta(newDelta);
107127           } else {
107128             this._edgeList.add(e);
107129             e.setDepthDelta(BufferBuilder.depthDelta(e.getLabel()));
107130           }
107131         };
107132         BufferBuilder.prototype.buildSubgraphs = function buildSubgraphs (subgraphList, polyBuilder) {
107133           var processedGraphs = new ArrayList();
107134           for (var i = subgraphList.iterator(); i.hasNext();) {
107135             var subgraph = i.next();
107136             var p = subgraph.getRightmostCoordinate();
107137             var locater = new SubgraphDepthLocater(processedGraphs);
107138             var outsideDepth = locater.getDepth(p);
107139             subgraph.computeDepth(outsideDepth);
107140             subgraph.findResultEdges();
107141             processedGraphs.add(subgraph);
107142             polyBuilder.add(subgraph.getDirectedEdges(), subgraph.getNodes());
107143           }
107144         };
107145         BufferBuilder.prototype.createSubgraphs = function createSubgraphs (graph) {
107146           var subgraphList = new ArrayList();
107147           for (var i = graph.getNodes().iterator(); i.hasNext();) {
107148             var node = i.next();
107149             if (!node.isVisited()) {
107150               var subgraph = new BufferSubgraph();
107151               subgraph.create(node);
107152               subgraphList.add(subgraph);
107153             }
107154           }
107155           Collections.sort(subgraphList, Collections.reverseOrder());
107156           return subgraphList
107157         };
107158         BufferBuilder.prototype.createEmptyResultGeometry = function createEmptyResultGeometry () {
107159           var emptyGeom = this._geomFact.createPolygon();
107160           return emptyGeom
107161         };
107162         BufferBuilder.prototype.getNoder = function getNoder (precisionModel) {
107163           if (this._workingNoder !== null) { return this._workingNoder }
107164           var noder = new MCIndexNoder();
107165           var li = new RobustLineIntersector();
107166           li.setPrecisionModel(precisionModel);
107167           noder.setSegmentIntersector(new IntersectionAdder(li));
107168           return noder
107169         };
107170         BufferBuilder.prototype.buffer = function buffer (g, distance) {
107171           var precisionModel = this._workingPrecisionModel;
107172           if (precisionModel === null) { precisionModel = g.getPrecisionModel(); }
107173           this._geomFact = g.getFactory();
107174           var curveBuilder = new OffsetCurveBuilder(precisionModel, this._bufParams);
107175           var curveSetBuilder = new OffsetCurveSetBuilder(g, distance, curveBuilder);
107176           var bufferSegStrList = curveSetBuilder.getCurves();
107177           if (bufferSegStrList.size() <= 0) {
107178             return this.createEmptyResultGeometry()
107179           }
107180           this.computeNodedEdges(bufferSegStrList, precisionModel);
107181           this._graph = new PlanarGraph(new OverlayNodeFactory());
107182           this._graph.addEdges(this._edgeList.getEdges());
107183           var subgraphList = this.createSubgraphs(this._graph);
107184           var polyBuilder = new PolygonBuilder(this._geomFact);
107185           this.buildSubgraphs(subgraphList, polyBuilder);
107186           var resultPolyList = polyBuilder.getPolygons();
107187           if (resultPolyList.size() <= 0) {
107188             return this.createEmptyResultGeometry()
107189           }
107190           var resultGeom = this._geomFact.buildGeometry(resultPolyList);
107191           return resultGeom
107192         };
107193         BufferBuilder.prototype.computeNodedEdges = function computeNodedEdges (bufferSegStrList, precisionModel) {
107194             var this$1 = this;
107195
107196           var noder = this.getNoder(precisionModel);
107197           noder.computeNodes(bufferSegStrList);
107198           var nodedSegStrings = noder.getNodedSubstrings();
107199           for (var i = nodedSegStrings.iterator(); i.hasNext();) {
107200             var segStr = i.next();
107201             var pts = segStr.getCoordinates();
107202             if (pts.length === 2 && pts[0].equals2D(pts[1])) { continue }
107203             var oldLabel = segStr.getData();
107204             var edge = new Edge(segStr.getCoordinates(), new Label(oldLabel));
107205             this$1.insertUniqueEdge(edge);
107206           }
107207         };
107208         BufferBuilder.prototype.setNoder = function setNoder (noder) {
107209           this._workingNoder = noder;
107210         };
107211         BufferBuilder.prototype.interfaces_ = function interfaces_ () {
107212           return []
107213         };
107214         BufferBuilder.prototype.getClass = function getClass () {
107215           return BufferBuilder
107216         };
107217         BufferBuilder.depthDelta = function depthDelta (label) {
107218           var lLoc = label.getLocation(0, Position.LEFT);
107219           var rLoc = label.getLocation(0, Position.RIGHT);
107220           if (lLoc === Location.INTERIOR && rLoc === Location.EXTERIOR) { return 1; } else if (lLoc === Location.EXTERIOR && rLoc === Location.INTERIOR) { return -1 }
107221           return 0
107222         };
107223         BufferBuilder.convertSegStrings = function convertSegStrings (it) {
107224           var fact = new GeometryFactory();
107225           var lines = new ArrayList();
107226           while (it.hasNext()) {
107227             var ss = it.next();
107228             var line = fact.createLineString(ss.getCoordinates());
107229             lines.add(line);
107230           }
107231           return fact.buildGeometry(lines)
107232         };
107233
107234         var ScaledNoder = function ScaledNoder () {
107235           this._noder = null;
107236           this._scaleFactor = null;
107237           this._offsetX = null;
107238           this._offsetY = null;
107239           this._isScaled = false;
107240           if (arguments.length === 2) {
107241             var noder = arguments[0];
107242             var scaleFactor = arguments[1];
107243             this._noder = noder;
107244             this._scaleFactor = scaleFactor;
107245             this._offsetX = 0.0;
107246             this._offsetY = 0.0;
107247             this._isScaled = !this.isIntegerPrecision();
107248           } else if (arguments.length === 4) {
107249             var noder$1 = arguments[0];
107250             var scaleFactor$1 = arguments[1];
107251             var offsetX = arguments[2];
107252             var offsetY = arguments[3];
107253             this._noder = noder$1;
107254             this._scaleFactor = scaleFactor$1;
107255             this._offsetX = offsetX;
107256             this._offsetY = offsetY;
107257             this._isScaled = !this.isIntegerPrecision();
107258           }
107259         };
107260         ScaledNoder.prototype.rescale = function rescale () {
107261             var this$1 = this;
107262
107263           if (hasInterface(arguments[0], Collection)) {
107264             var segStrings = arguments[0];
107265             for (var i = segStrings.iterator(); i.hasNext();) {
107266               var ss = i.next();
107267               this$1.rescale(ss.getCoordinates());
107268             }
107269           } else if (arguments[0] instanceof Array) {
107270             var pts = arguments[0];
107271             // let p0 = null
107272             // let p1 = null
107273             // if (pts.length === 2) {
107274             // p0 = new Coordinate(pts[0])
107275             // p1 = new Coordinate(pts[1])
107276             // }
107277             for (var i$1 = 0; i$1 < pts.length; i$1++) {
107278               pts[i$1].x = pts[i$1].x / this$1._scaleFactor + this$1._offsetX;
107279               pts[i$1].y = pts[i$1].y / this$1._scaleFactor + this$1._offsetY;
107280             }
107281             if (pts.length === 2 && pts[0].equals2D(pts[1])) {
107282               System.out.println(pts);
107283             }
107284           }
107285         };
107286         ScaledNoder.prototype.scale = function scale () {
107287             var this$1 = this;
107288
107289           if (hasInterface(arguments[0], Collection)) {
107290             var segStrings = arguments[0];
107291             var nodedSegmentStrings = new ArrayList();
107292             for (var i = segStrings.iterator(); i.hasNext();) {
107293               var ss = i.next();
107294               nodedSegmentStrings.add(new NodedSegmentString(this$1.scale(ss.getCoordinates()), ss.getData()));
107295             }
107296             return nodedSegmentStrings
107297           } else if (arguments[0] instanceof Array) {
107298             var pts = arguments[0];
107299             var roundPts = new Array(pts.length).fill(null);
107300             for (var i$1 = 0; i$1 < pts.length; i$1++) {
107301               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);
107302             }
107303             var roundPtsNoDup = CoordinateArrays.removeRepeatedPoints(roundPts);
107304             return roundPtsNoDup
107305           }
107306         };
107307         ScaledNoder.prototype.isIntegerPrecision = function isIntegerPrecision () {
107308           return this._scaleFactor === 1.0
107309         };
107310         ScaledNoder.prototype.getNodedSubstrings = function getNodedSubstrings () {
107311           var splitSS = this._noder.getNodedSubstrings();
107312           if (this._isScaled) { this.rescale(splitSS); }
107313           return splitSS
107314         };
107315         ScaledNoder.prototype.computeNodes = function computeNodes (inputSegStrings) {
107316           var intSegStrings = inputSegStrings;
107317           if (this._isScaled) { intSegStrings = this.scale(inputSegStrings); }
107318           this._noder.computeNodes(intSegStrings);
107319         };
107320         ScaledNoder.prototype.interfaces_ = function interfaces_ () {
107321           return [Noder]
107322         };
107323         ScaledNoder.prototype.getClass = function getClass () {
107324           return ScaledNoder
107325         };
107326
107327         var NodingValidator = function NodingValidator () {
107328           this._li = new RobustLineIntersector();
107329           this._segStrings = null;
107330           var segStrings = arguments[0];
107331           this._segStrings = segStrings;
107332         };
107333
107334         var staticAccessors$33 = { fact: { configurable: true } };
107335         NodingValidator.prototype.checkEndPtVertexIntersections = function checkEndPtVertexIntersections () {
107336             var this$1 = this;
107337
107338           if (arguments.length === 0) {
107339             for (var i = this._segStrings.iterator(); i.hasNext();) {
107340               var ss = i.next();
107341               var pts = ss.getCoordinates();
107342               this$1.checkEndPtVertexIntersections(pts[0], this$1._segStrings);
107343               this$1.checkEndPtVertexIntersections(pts[pts.length - 1], this$1._segStrings);
107344             }
107345           } else if (arguments.length === 2) {
107346             var testPt = arguments[0];
107347             var segStrings = arguments[1];
107348             for (var i$1 = segStrings.iterator(); i$1.hasNext();) {
107349               var ss$1 = i$1.next();
107350               var pts$1 = ss$1.getCoordinates();
107351               for (var j = 1; j < pts$1.length - 1; j++) {
107352                 if (pts$1[j].equals(testPt)) { throw new RuntimeException('found endpt/interior pt intersection at index ' + j + ' :pt ' + testPt) }
107353               }
107354             }
107355           }
107356         };
107357         NodingValidator.prototype.checkInteriorIntersections = function checkInteriorIntersections () {
107358             var this$1 = this;
107359
107360           if (arguments.length === 0) {
107361             for (var i = this._segStrings.iterator(); i.hasNext();) {
107362               var ss0 = i.next();
107363               for (var j = this._segStrings.iterator(); j.hasNext();) {
107364                 var ss1 = j.next();
107365                 this$1.checkInteriorIntersections(ss0, ss1);
107366               }
107367             }
107368           } else if (arguments.length === 2) {
107369             var ss0$1 = arguments[0];
107370             var ss1$1 = arguments[1];
107371             var pts0 = ss0$1.getCoordinates();
107372             var pts1 = ss1$1.getCoordinates();
107373             for (var i0 = 0; i0 < pts0.length - 1; i0++) {
107374               for (var i1 = 0; i1 < pts1.length - 1; i1++) {
107375                 this$1.checkInteriorIntersections(ss0$1, i0, ss1$1, i1);
107376               }
107377             }
107378           } else if (arguments.length === 4) {
107379             var e0 = arguments[0];
107380             var segIndex0 = arguments[1];
107381             var e1 = arguments[2];
107382             var segIndex1 = arguments[3];
107383             if (e0 === e1 && segIndex0 === segIndex1) { return null }
107384             var p00 = e0.getCoordinates()[segIndex0];
107385             var p01 = e0.getCoordinates()[segIndex0 + 1];
107386             var p10 = e1.getCoordinates()[segIndex1];
107387             var p11 = e1.getCoordinates()[segIndex1 + 1];
107388             this._li.computeIntersection(p00, p01, p10, p11);
107389             if (this._li.hasIntersection()) {
107390               if (this._li.isProper() || this.hasInteriorIntersection(this._li, p00, p01) || this.hasInteriorIntersection(this._li, p10, p11)) {
107391                 throw new RuntimeException('found non-noded intersection at ' + p00 + '-' + p01 + ' and ' + p10 + '-' + p11)
107392               }
107393             }
107394           }
107395         };
107396         NodingValidator.prototype.checkValid = function checkValid () {
107397           this.checkEndPtVertexIntersections();
107398           this.checkInteriorIntersections();
107399           this.checkCollapses();
107400         };
107401         NodingValidator.prototype.checkCollapses = function checkCollapses () {
107402             var this$1 = this;
107403
107404           if (arguments.length === 0) {
107405             for (var i = this._segStrings.iterator(); i.hasNext();) {
107406               var ss = i.next();
107407               this$1.checkCollapses(ss);
107408             }
107409           } else if (arguments.length === 1) {
107410             var ss$1 = arguments[0];
107411             var pts = ss$1.getCoordinates();
107412             for (var i$1 = 0; i$1 < pts.length - 2; i$1++) {
107413               this$1.checkCollapse(pts[i$1], pts[i$1 + 1], pts[i$1 + 2]);
107414             }
107415           }
107416         };
107417         NodingValidator.prototype.hasInteriorIntersection = function hasInteriorIntersection (li, p0, p1) {
107418           for (var i = 0; i < li.getIntersectionNum(); i++) {
107419             var intPt = li.getIntersection(i);
107420             if (!(intPt.equals(p0) || intPt.equals(p1))) { return true }
107421           }
107422           return false
107423         };
107424         NodingValidator.prototype.checkCollapse = function checkCollapse (p0, p1, p2) {
107425           if (p0.equals(p2)) { throw new RuntimeException('found non-noded collapse at ' + NodingValidator.fact.createLineString([p0, p1, p2])) }
107426         };
107427         NodingValidator.prototype.interfaces_ = function interfaces_ () {
107428           return []
107429         };
107430         NodingValidator.prototype.getClass = function getClass () {
107431           return NodingValidator
107432         };
107433         staticAccessors$33.fact.get = function () { return new GeometryFactory() };
107434
107435         Object.defineProperties( NodingValidator, staticAccessors$33 );
107436
107437         var HotPixel = function HotPixel () {
107438           this._li = null;
107439           this._pt = null;
107440           this._originalPt = null;
107441           this._ptScaled = null;
107442           this._p0Scaled = null;
107443           this._p1Scaled = null;
107444           this._scaleFactor = null;
107445           this._minx = null;
107446           this._maxx = null;
107447           this._miny = null;
107448           this._maxy = null;
107449           this._corner = new Array(4).fill(null);
107450           this._safeEnv = null;
107451           var pt = arguments[0];
107452           var scaleFactor = arguments[1];
107453           var li = arguments[2];
107454           this._originalPt = pt;
107455           this._pt = pt;
107456           this._scaleFactor = scaleFactor;
107457           this._li = li;
107458           if (scaleFactor <= 0) { throw new IllegalArgumentException('Scale factor must be non-zero') }
107459           if (scaleFactor !== 1.0) {
107460             this._pt = new Coordinate(this.scale(pt.x), this.scale(pt.y));
107461             this._p0Scaled = new Coordinate();
107462             this._p1Scaled = new Coordinate();
107463           }
107464           this.initCorners(this._pt);
107465         };
107466
107467         var staticAccessors$34 = { SAFE_ENV_EXPANSION_FACTOR: { configurable: true } };
107468         HotPixel.prototype.intersectsScaled = function intersectsScaled (p0, p1) {
107469           var segMinx = Math.min(p0.x, p1.x);
107470           var segMaxx = Math.max(p0.x, p1.x);
107471           var segMiny = Math.min(p0.y, p1.y);
107472           var segMaxy = Math.max(p0.y, p1.y);
107473           var isOutsidePixelEnv = this._maxx < segMinx || this._minx > segMaxx || this._maxy < segMiny || this._miny > segMaxy;
107474           if (isOutsidePixelEnv) { return false }
107475           var intersects = this.intersectsToleranceSquare(p0, p1);
107476           Assert.isTrue(!(isOutsidePixelEnv && intersects), 'Found bad envelope test');
107477           return intersects
107478         };
107479         HotPixel.prototype.initCorners = function initCorners (pt) {
107480           var tolerance = 0.5;
107481           this._minx = pt.x - tolerance;
107482           this._maxx = pt.x + tolerance;
107483           this._miny = pt.y - tolerance;
107484           this._maxy = pt.y + tolerance;
107485           this._corner[0] = new Coordinate(this._maxx, this._maxy);
107486           this._corner[1] = new Coordinate(this._minx, this._maxy);
107487           this._corner[2] = new Coordinate(this._minx, this._miny);
107488           this._corner[3] = new Coordinate(this._maxx, this._miny);
107489         };
107490         HotPixel.prototype.intersects = function intersects (p0, p1) {
107491           if (this._scaleFactor === 1.0) { return this.intersectsScaled(p0, p1) }
107492           this.copyScaled(p0, this._p0Scaled);
107493           this.copyScaled(p1, this._p1Scaled);
107494           return this.intersectsScaled(this._p0Scaled, this._p1Scaled)
107495         };
107496         HotPixel.prototype.scale = function scale (val) {
107497           return Math.round(val * this._scaleFactor)
107498         };
107499         HotPixel.prototype.getCoordinate = function getCoordinate () {
107500           return this._originalPt
107501         };
107502         HotPixel.prototype.copyScaled = function copyScaled (p, pScaled) {
107503           pScaled.x = this.scale(p.x);
107504           pScaled.y = this.scale(p.y);
107505         };
107506         HotPixel.prototype.getSafeEnvelope = function getSafeEnvelope () {
107507           if (this._safeEnv === null) {
107508             var safeTolerance = HotPixel.SAFE_ENV_EXPANSION_FACTOR / this._scaleFactor;
107509             this._safeEnv = new Envelope(this._originalPt.x - safeTolerance, this._originalPt.x + safeTolerance, this._originalPt.y - safeTolerance, this._originalPt.y + safeTolerance);
107510           }
107511           return this._safeEnv
107512         };
107513         HotPixel.prototype.intersectsPixelClosure = function intersectsPixelClosure (p0, p1) {
107514           this._li.computeIntersection(p0, p1, this._corner[0], this._corner[1]);
107515           if (this._li.hasIntersection()) { return true }
107516           this._li.computeIntersection(p0, p1, this._corner[1], this._corner[2]);
107517           if (this._li.hasIntersection()) { return true }
107518           this._li.computeIntersection(p0, p1, this._corner[2], this._corner[3]);
107519           if (this._li.hasIntersection()) { return true }
107520           this._li.computeIntersection(p0, p1, this._corner[3], this._corner[0]);
107521           if (this._li.hasIntersection()) { return true }
107522           return false
107523         };
107524         HotPixel.prototype.intersectsToleranceSquare = function intersectsToleranceSquare (p0, p1) {
107525           var intersectsLeft = false;
107526           var intersectsBottom = false;
107527           this._li.computeIntersection(p0, p1, this._corner[0], this._corner[1]);
107528           if (this._li.isProper()) { return true }
107529           this._li.computeIntersection(p0, p1, this._corner[1], this._corner[2]);
107530           if (this._li.isProper()) { return true }
107531           if (this._li.hasIntersection()) { intersectsLeft = true; }
107532           this._li.computeIntersection(p0, p1, this._corner[2], this._corner[3]);
107533           if (this._li.isProper()) { return true }
107534           if (this._li.hasIntersection()) { intersectsBottom = true; }
107535           this._li.computeIntersection(p0, p1, this._corner[3], this._corner[0]);
107536           if (this._li.isProper()) { return true }
107537           if (intersectsLeft && intersectsBottom) { return true }
107538           if (p0.equals(this._pt)) { return true }
107539           if (p1.equals(this._pt)) { return true }
107540           return false
107541         };
107542         HotPixel.prototype.addSnappedNode = function addSnappedNode (segStr, segIndex) {
107543           var p0 = segStr.getCoordinate(segIndex);
107544           var p1 = segStr.getCoordinate(segIndex + 1);
107545           if (this.intersects(p0, p1)) {
107546             segStr.addIntersection(this.getCoordinate(), segIndex);
107547             return true
107548           }
107549           return false
107550         };
107551         HotPixel.prototype.interfaces_ = function interfaces_ () {
107552           return []
107553         };
107554         HotPixel.prototype.getClass = function getClass () {
107555           return HotPixel
107556         };
107557         staticAccessors$34.SAFE_ENV_EXPANSION_FACTOR.get = function () { return 0.75 };
107558
107559         Object.defineProperties( HotPixel, staticAccessors$34 );
107560
107561         var MonotoneChainSelectAction = function MonotoneChainSelectAction () {
107562           this.tempEnv1 = new Envelope();
107563           this.selectedSegment = new LineSegment();
107564         };
107565         MonotoneChainSelectAction.prototype.select = function select () {
107566           if (arguments.length === 1) ; else if (arguments.length === 2) {
107567             var mc = arguments[0];
107568             var startIndex = arguments[1];
107569             mc.getLineSegment(startIndex, this.selectedSegment);
107570             this.select(this.selectedSegment);
107571           }
107572         };
107573         MonotoneChainSelectAction.prototype.interfaces_ = function interfaces_ () {
107574           return []
107575         };
107576         MonotoneChainSelectAction.prototype.getClass = function getClass () {
107577           return MonotoneChainSelectAction
107578         };
107579
107580         var MCIndexPointSnapper = function MCIndexPointSnapper () {
107581           this._index = null;
107582           var index = arguments[0];
107583           this._index = index;
107584         };
107585
107586         var staticAccessors$35 = { HotPixelSnapAction: { configurable: true } };
107587         MCIndexPointSnapper.prototype.snap = function snap () {
107588           if (arguments.length === 1) {
107589             var hotPixel = arguments[0];
107590             return this.snap(hotPixel, null, -1)
107591           } else if (arguments.length === 3) {
107592             var hotPixel$1 = arguments[0];
107593             var parentEdge = arguments[1];
107594             var hotPixelVertexIndex = arguments[2];
107595             var pixelEnv = hotPixel$1.getSafeEnvelope();
107596             var hotPixelSnapAction = new HotPixelSnapAction(hotPixel$1, parentEdge, hotPixelVertexIndex);
107597             this._index.query(pixelEnv, {
107598               interfaces_: function () {
107599                 return [ItemVisitor]
107600               },
107601               visitItem: function (item) {
107602                 var testChain = item;
107603                 testChain.select(pixelEnv, hotPixelSnapAction);
107604               }
107605             });
107606             return hotPixelSnapAction.isNodeAdded()
107607           }
107608         };
107609         MCIndexPointSnapper.prototype.interfaces_ = function interfaces_ () {
107610           return []
107611         };
107612         MCIndexPointSnapper.prototype.getClass = function getClass () {
107613           return MCIndexPointSnapper
107614         };
107615         staticAccessors$35.HotPixelSnapAction.get = function () { return HotPixelSnapAction };
107616
107617         Object.defineProperties( MCIndexPointSnapper, staticAccessors$35 );
107618
107619         var HotPixelSnapAction = (function (MonotoneChainSelectAction$$1) {
107620           function HotPixelSnapAction () {
107621             MonotoneChainSelectAction$$1.call(this);
107622             this._hotPixel = null;
107623             this._parentEdge = null;
107624             this._hotPixelVertexIndex = null;
107625             this._isNodeAdded = false;
107626             var hotPixel = arguments[0];
107627             var parentEdge = arguments[1];
107628             var hotPixelVertexIndex = arguments[2];
107629             this._hotPixel = hotPixel;
107630             this._parentEdge = parentEdge;
107631             this._hotPixelVertexIndex = hotPixelVertexIndex;
107632           }
107633
107634           if ( MonotoneChainSelectAction$$1 ) HotPixelSnapAction.__proto__ = MonotoneChainSelectAction$$1;
107635           HotPixelSnapAction.prototype = Object.create( MonotoneChainSelectAction$$1 && MonotoneChainSelectAction$$1.prototype );
107636           HotPixelSnapAction.prototype.constructor = HotPixelSnapAction;
107637           HotPixelSnapAction.prototype.isNodeAdded = function isNodeAdded () {
107638             return this._isNodeAdded
107639           };
107640           HotPixelSnapAction.prototype.select = function select () {
107641             if (arguments.length === 2) {
107642               var mc = arguments[0];
107643               var startIndex = arguments[1];
107644               var ss = mc.getContext();
107645               if (this._parentEdge !== null) {
107646                 if (ss === this._parentEdge && startIndex === this._hotPixelVertexIndex) { return null }
107647               }
107648               this._isNodeAdded = this._hotPixel.addSnappedNode(ss, startIndex);
107649             } else { return MonotoneChainSelectAction$$1.prototype.select.apply(this, arguments) }
107650           };
107651           HotPixelSnapAction.prototype.interfaces_ = function interfaces_ () {
107652             return []
107653           };
107654           HotPixelSnapAction.prototype.getClass = function getClass () {
107655             return HotPixelSnapAction
107656           };
107657
107658           return HotPixelSnapAction;
107659         }(MonotoneChainSelectAction));
107660
107661         var InteriorIntersectionFinderAdder = function InteriorIntersectionFinderAdder () {
107662           this._li = null;
107663           this._interiorIntersections = null;
107664           var li = arguments[0];
107665           this._li = li;
107666           this._interiorIntersections = new ArrayList();
107667         };
107668         InteriorIntersectionFinderAdder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
107669             var this$1 = this;
107670
107671           if (e0 === e1 && segIndex0 === segIndex1) { return null }
107672           var p00 = e0.getCoordinates()[segIndex0];
107673           var p01 = e0.getCoordinates()[segIndex0 + 1];
107674           var p10 = e1.getCoordinates()[segIndex1];
107675           var p11 = e1.getCoordinates()[segIndex1 + 1];
107676           this._li.computeIntersection(p00, p01, p10, p11);
107677           if (this._li.hasIntersection()) {
107678             if (this._li.isInteriorIntersection()) {
107679               for (var intIndex = 0; intIndex < this._li.getIntersectionNum(); intIndex++) {
107680                 this$1._interiorIntersections.add(this$1._li.getIntersection(intIndex));
107681               }
107682               e0.addIntersections(this._li, segIndex0, 0);
107683               e1.addIntersections(this._li, segIndex1, 1);
107684             }
107685           }
107686         };
107687         InteriorIntersectionFinderAdder.prototype.isDone = function isDone () {
107688           return false
107689         };
107690         InteriorIntersectionFinderAdder.prototype.getInteriorIntersections = function getInteriorIntersections () {
107691           return this._interiorIntersections
107692         };
107693         InteriorIntersectionFinderAdder.prototype.interfaces_ = function interfaces_ () {
107694           return [SegmentIntersector]
107695         };
107696         InteriorIntersectionFinderAdder.prototype.getClass = function getClass () {
107697           return InteriorIntersectionFinderAdder
107698         };
107699
107700         var MCIndexSnapRounder = function MCIndexSnapRounder () {
107701           this._pm = null;
107702           this._li = null;
107703           this._scaleFactor = null;
107704           this._noder = null;
107705           this._pointSnapper = null;
107706           this._nodedSegStrings = null;
107707           var pm = arguments[0];
107708           this._pm = pm;
107709           this._li = new RobustLineIntersector();
107710           this._li.setPrecisionModel(pm);
107711           this._scaleFactor = pm.getScale();
107712         };
107713         MCIndexSnapRounder.prototype.checkCorrectness = function checkCorrectness (inputSegmentStrings) {
107714           var resultSegStrings = NodedSegmentString.getNodedSubstrings(inputSegmentStrings);
107715           var nv = new NodingValidator(resultSegStrings);
107716           try {
107717             nv.checkValid();
107718           } catch (ex) {
107719             if (ex instanceof Exception) {
107720               ex.printStackTrace();
107721             } else { throw ex }
107722           } finally {}
107723         };
107724         MCIndexSnapRounder.prototype.getNodedSubstrings = function getNodedSubstrings () {
107725           return NodedSegmentString.getNodedSubstrings(this._nodedSegStrings)
107726         };
107727         MCIndexSnapRounder.prototype.snapRound = function snapRound (segStrings, li) {
107728           var intersections = this.findInteriorIntersections(segStrings, li);
107729           this.computeIntersectionSnaps(intersections);
107730           this.computeVertexSnaps(segStrings);
107731         };
107732         MCIndexSnapRounder.prototype.findInteriorIntersections = function findInteriorIntersections (segStrings, li) {
107733           var intFinderAdder = new InteriorIntersectionFinderAdder(li);
107734           this._noder.setSegmentIntersector(intFinderAdder);
107735           this._noder.computeNodes(segStrings);
107736           return intFinderAdder.getInteriorIntersections()
107737         };
107738         MCIndexSnapRounder.prototype.computeVertexSnaps = function computeVertexSnaps () {
107739             var this$1 = this;
107740
107741           if (hasInterface(arguments[0], Collection)) {
107742             var edges = arguments[0];
107743             for (var i0 = edges.iterator(); i0.hasNext();) {
107744               var edge0 = i0.next();
107745               this$1.computeVertexSnaps(edge0);
107746             }
107747           } else if (arguments[0] instanceof NodedSegmentString) {
107748             var e = arguments[0];
107749             var pts0 = e.getCoordinates();
107750             for (var i = 0; i < pts0.length; i++) {
107751               var hotPixel = new HotPixel(pts0[i], this$1._scaleFactor, this$1._li);
107752               var isNodeAdded = this$1._pointSnapper.snap(hotPixel, e, i);
107753               if (isNodeAdded) {
107754                 e.addIntersection(pts0[i], i);
107755               }
107756             }
107757           }
107758         };
107759         MCIndexSnapRounder.prototype.computeNodes = function computeNodes (inputSegmentStrings) {
107760           this._nodedSegStrings = inputSegmentStrings;
107761           this._noder = new MCIndexNoder();
107762           this._pointSnapper = new MCIndexPointSnapper(this._noder.getIndex());
107763           this.snapRound(inputSegmentStrings, this._li);
107764         };
107765         MCIndexSnapRounder.prototype.computeIntersectionSnaps = function computeIntersectionSnaps (snapPts) {
107766             var this$1 = this;
107767
107768           for (var it = snapPts.iterator(); it.hasNext();) {
107769             var snapPt = it.next();
107770             var hotPixel = new HotPixel(snapPt, this$1._scaleFactor, this$1._li);
107771             this$1._pointSnapper.snap(hotPixel);
107772           }
107773         };
107774         MCIndexSnapRounder.prototype.interfaces_ = function interfaces_ () {
107775           return [Noder]
107776         };
107777         MCIndexSnapRounder.prototype.getClass = function getClass () {
107778           return MCIndexSnapRounder
107779         };
107780
107781         var BufferOp = function BufferOp () {
107782           this._argGeom = null;
107783           this._distance = null;
107784           this._bufParams = new BufferParameters();
107785           this._resultGeometry = null;
107786           this._saveException = null;
107787           if (arguments.length === 1) {
107788             var g = arguments[0];
107789             this._argGeom = g;
107790           } else if (arguments.length === 2) {
107791             var g$1 = arguments[0];
107792             var bufParams = arguments[1];
107793             this._argGeom = g$1;
107794             this._bufParams = bufParams;
107795           }
107796         };
107797
107798         var staticAccessors$32 = { CAP_ROUND: { configurable: true },CAP_BUTT: { configurable: true },CAP_FLAT: { configurable: true },CAP_SQUARE: { configurable: true },MAX_PRECISION_DIGITS: { configurable: true } };
107799         BufferOp.prototype.bufferFixedPrecision = function bufferFixedPrecision (fixedPM) {
107800           var noder = new ScaledNoder(new MCIndexSnapRounder(new PrecisionModel(1.0)), fixedPM.getScale());
107801           var bufBuilder = new BufferBuilder(this._bufParams);
107802           bufBuilder.setWorkingPrecisionModel(fixedPM);
107803           bufBuilder.setNoder(noder);
107804           this._resultGeometry = bufBuilder.buffer(this._argGeom, this._distance);
107805         };
107806         BufferOp.prototype.bufferReducedPrecision = function bufferReducedPrecision () {
107807             var this$1 = this;
107808
107809           if (arguments.length === 0) {
107810             for (var precDigits = BufferOp.MAX_PRECISION_DIGITS; precDigits >= 0; precDigits--) {
107811               try {
107812                 this$1.bufferReducedPrecision(precDigits);
107813               } catch (ex) {
107814                 if (ex instanceof TopologyException) {
107815                   this$1._saveException = ex;
107816                 } else { throw ex }
107817               } finally {}
107818               if (this$1._resultGeometry !== null) { return null }
107819             }
107820             throw this._saveException
107821           } else if (arguments.length === 1) {
107822             var precisionDigits = arguments[0];
107823             var sizeBasedScaleFactor = BufferOp.precisionScaleFactor(this._argGeom, this._distance, precisionDigits);
107824             var fixedPM = new PrecisionModel(sizeBasedScaleFactor);
107825             this.bufferFixedPrecision(fixedPM);
107826           }
107827         };
107828         BufferOp.prototype.computeGeometry = function computeGeometry () {
107829           this.bufferOriginalPrecision();
107830           if (this._resultGeometry !== null) { return null }
107831           var argPM = this._argGeom.getFactory().getPrecisionModel();
107832           if (argPM.getType() === PrecisionModel.FIXED) { this.bufferFixedPrecision(argPM); } else { this.bufferReducedPrecision(); }
107833         };
107834         BufferOp.prototype.setQuadrantSegments = function setQuadrantSegments (quadrantSegments) {
107835           this._bufParams.setQuadrantSegments(quadrantSegments);
107836         };
107837         BufferOp.prototype.bufferOriginalPrecision = function bufferOriginalPrecision () {
107838           try {
107839             var bufBuilder = new BufferBuilder(this._bufParams);
107840             this._resultGeometry = bufBuilder.buffer(this._argGeom, this._distance);
107841           } catch (ex) {
107842             if (ex instanceof RuntimeException) {
107843               this._saveException = ex;
107844             } else { throw ex }
107845           } finally {}
107846         };
107847         BufferOp.prototype.getResultGeometry = function getResultGeometry (distance) {
107848           this._distance = distance;
107849           this.computeGeometry();
107850           return this._resultGeometry
107851         };
107852         BufferOp.prototype.setEndCapStyle = function setEndCapStyle (endCapStyle) {
107853           this._bufParams.setEndCapStyle(endCapStyle);
107854         };
107855         BufferOp.prototype.interfaces_ = function interfaces_ () {
107856           return []
107857         };
107858         BufferOp.prototype.getClass = function getClass () {
107859           return BufferOp
107860         };
107861         BufferOp.bufferOp = function bufferOp () {
107862           if (arguments.length === 2) {
107863             var g = arguments[0];
107864             var distance = arguments[1];
107865             var gBuf = new BufferOp(g);
107866             var geomBuf = gBuf.getResultGeometry(distance);
107867             return geomBuf
107868           } else if (arguments.length === 3) {
107869             if (Number.isInteger(arguments[2]) && (arguments[0] instanceof Geometry && typeof arguments[1] === 'number')) {
107870               var g$1 = arguments[0];
107871               var distance$1 = arguments[1];
107872               var quadrantSegments = arguments[2];
107873               var bufOp = new BufferOp(g$1);
107874               bufOp.setQuadrantSegments(quadrantSegments);
107875               var geomBuf$1 = bufOp.getResultGeometry(distance$1);
107876               return geomBuf$1
107877             } else if (arguments[2] instanceof BufferParameters && (arguments[0] instanceof Geometry && typeof arguments[1] === 'number')) {
107878               var g$2 = arguments[0];
107879               var distance$2 = arguments[1];
107880               var params = arguments[2];
107881               var bufOp$1 = new BufferOp(g$2, params);
107882               var geomBuf$2 = bufOp$1.getResultGeometry(distance$2);
107883               return geomBuf$2
107884             }
107885           } else if (arguments.length === 4) {
107886             var g$3 = arguments[0];
107887             var distance$3 = arguments[1];
107888             var quadrantSegments$1 = arguments[2];
107889             var endCapStyle = arguments[3];
107890             var bufOp$2 = new BufferOp(g$3);
107891             bufOp$2.setQuadrantSegments(quadrantSegments$1);
107892             bufOp$2.setEndCapStyle(endCapStyle);
107893             var geomBuf$3 = bufOp$2.getResultGeometry(distance$3);
107894             return geomBuf$3
107895           }
107896         };
107897         BufferOp.precisionScaleFactor = function precisionScaleFactor (g, distance, maxPrecisionDigits) {
107898           var env = g.getEnvelopeInternal();
107899           var envMax = MathUtil.max(Math.abs(env.getMaxX()), Math.abs(env.getMaxY()), Math.abs(env.getMinX()), Math.abs(env.getMinY()));
107900           var expandByDistance = distance > 0.0 ? distance : 0.0;
107901           var bufEnvMax = envMax + 2 * expandByDistance;
107902           var bufEnvPrecisionDigits = Math.trunc(Math.log(bufEnvMax) / Math.log(10) + 1.0);
107903           var minUnitLog10 = maxPrecisionDigits - bufEnvPrecisionDigits;
107904           var scaleFactor = Math.pow(10.0, minUnitLog10);
107905           return scaleFactor
107906         };
107907         staticAccessors$32.CAP_ROUND.get = function () { return BufferParameters.CAP_ROUND };
107908         staticAccessors$32.CAP_BUTT.get = function () { return BufferParameters.CAP_FLAT };
107909         staticAccessors$32.CAP_FLAT.get = function () { return BufferParameters.CAP_FLAT };
107910         staticAccessors$32.CAP_SQUARE.get = function () { return BufferParameters.CAP_SQUARE };
107911         staticAccessors$32.MAX_PRECISION_DIGITS.get = function () { return 12 };
107912
107913         Object.defineProperties( BufferOp, staticAccessors$32 );
107914
107915         var PointPairDistance = function PointPairDistance () {
107916           this._pt = [new Coordinate(), new Coordinate()];
107917           this._distance = Double.NaN;
107918           this._isNull = true;
107919         };
107920         PointPairDistance.prototype.getCoordinates = function getCoordinates () {
107921           return this._pt
107922         };
107923         PointPairDistance.prototype.getCoordinate = function getCoordinate (i) {
107924           return this._pt[i]
107925         };
107926         PointPairDistance.prototype.setMinimum = function setMinimum () {
107927           if (arguments.length === 1) {
107928             var ptDist = arguments[0];
107929             this.setMinimum(ptDist._pt[0], ptDist._pt[1]);
107930           } else if (arguments.length === 2) {
107931             var p0 = arguments[0];
107932             var p1 = arguments[1];
107933             if (this._isNull) {
107934               this.initialize(p0, p1);
107935               return null
107936             }
107937             var dist = p0.distance(p1);
107938             if (dist < this._distance) { this.initialize(p0, p1, dist); }
107939           }
107940         };
107941         PointPairDistance.prototype.initialize = function initialize () {
107942           if (arguments.length === 0) {
107943             this._isNull = true;
107944           } else if (arguments.length === 2) {
107945             var p0 = arguments[0];
107946             var p1 = arguments[1];
107947             this._pt[0].setCoordinate(p0);
107948             this._pt[1].setCoordinate(p1);
107949             this._distance = p0.distance(p1);
107950             this._isNull = false;
107951           } else if (arguments.length === 3) {
107952             var p0$1 = arguments[0];
107953             var p1$1 = arguments[1];
107954             var distance = arguments[2];
107955             this._pt[0].setCoordinate(p0$1);
107956             this._pt[1].setCoordinate(p1$1);
107957             this._distance = distance;
107958             this._isNull = false;
107959           }
107960         };
107961         PointPairDistance.prototype.getDistance = function getDistance () {
107962           return this._distance
107963         };
107964         PointPairDistance.prototype.setMaximum = function setMaximum () {
107965           if (arguments.length === 1) {
107966             var ptDist = arguments[0];
107967             this.setMaximum(ptDist._pt[0], ptDist._pt[1]);
107968           } else if (arguments.length === 2) {
107969             var p0 = arguments[0];
107970             var p1 = arguments[1];
107971             if (this._isNull) {
107972               this.initialize(p0, p1);
107973               return null
107974             }
107975             var dist = p0.distance(p1);
107976             if (dist > this._distance) { this.initialize(p0, p1, dist); }
107977           }
107978         };
107979         PointPairDistance.prototype.interfaces_ = function interfaces_ () {
107980           return []
107981         };
107982         PointPairDistance.prototype.getClass = function getClass () {
107983           return PointPairDistance
107984         };
107985
107986         var DistanceToPointFinder = function DistanceToPointFinder () {};
107987
107988         DistanceToPointFinder.prototype.interfaces_ = function interfaces_ () {
107989           return []
107990         };
107991         DistanceToPointFinder.prototype.getClass = function getClass () {
107992           return DistanceToPointFinder
107993         };
107994         DistanceToPointFinder.computeDistance = function computeDistance () {
107995           if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof LineString && arguments[1] instanceof Coordinate)) {
107996             var line = arguments[0];
107997             var pt = arguments[1];
107998             var ptDist = arguments[2];
107999             var coords = line.getCoordinates();
108000             var tempSegment = new LineSegment();
108001             for (var i = 0; i < coords.length - 1; i++) {
108002               tempSegment.setCoordinates(coords[i], coords[i + 1]);
108003               var closestPt = tempSegment.closestPoint(pt);
108004               ptDist.setMinimum(closestPt, pt);
108005             }
108006           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof Polygon && arguments[1] instanceof Coordinate)) {
108007             var poly = arguments[0];
108008             var pt$1 = arguments[1];
108009             var ptDist$1 = arguments[2];
108010             DistanceToPointFinder.computeDistance(poly.getExteriorRing(), pt$1, ptDist$1);
108011             for (var i$1 = 0; i$1 < poly.getNumInteriorRing(); i$1++) {
108012               DistanceToPointFinder.computeDistance(poly.getInteriorRingN(i$1), pt$1, ptDist$1);
108013             }
108014           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof Geometry && arguments[1] instanceof Coordinate)) {
108015             var geom = arguments[0];
108016             var pt$2 = arguments[1];
108017             var ptDist$2 = arguments[2];
108018             if (geom instanceof LineString) {
108019               DistanceToPointFinder.computeDistance(geom, pt$2, ptDist$2);
108020             } else if (geom instanceof Polygon) {
108021               DistanceToPointFinder.computeDistance(geom, pt$2, ptDist$2);
108022             } else if (geom instanceof GeometryCollection) {
108023               var gc = geom;
108024               for (var i$2 = 0; i$2 < gc.getNumGeometries(); i$2++) {
108025                 var g = gc.getGeometryN(i$2);
108026                 DistanceToPointFinder.computeDistance(g, pt$2, ptDist$2);
108027               }
108028             } else {
108029               ptDist$2.setMinimum(geom.getCoordinate(), pt$2);
108030             }
108031           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof LineSegment && arguments[1] instanceof Coordinate)) {
108032             var segment = arguments[0];
108033             var pt$3 = arguments[1];
108034             var ptDist$3 = arguments[2];
108035             var closestPt$1 = segment.closestPoint(pt$3);
108036             ptDist$3.setMinimum(closestPt$1, pt$3);
108037           }
108038         };
108039
108040         var BufferCurveMaximumDistanceFinder = function BufferCurveMaximumDistanceFinder (inputGeom) {
108041           this._maxPtDist = new PointPairDistance();
108042           this._inputGeom = inputGeom || null;
108043         };
108044
108045         var staticAccessors$36 = { MaxPointDistanceFilter: { configurable: true },MaxMidpointDistanceFilter: { configurable: true } };
108046         BufferCurveMaximumDistanceFinder.prototype.computeMaxMidpointDistance = function computeMaxMidpointDistance (curve) {
108047           var distFilter = new MaxMidpointDistanceFilter(this._inputGeom);
108048           curve.apply(distFilter);
108049           this._maxPtDist.setMaximum(distFilter.getMaxPointDistance());
108050         };
108051         BufferCurveMaximumDistanceFinder.prototype.computeMaxVertexDistance = function computeMaxVertexDistance (curve) {
108052           var distFilter = new MaxPointDistanceFilter(this._inputGeom);
108053           curve.apply(distFilter);
108054           this._maxPtDist.setMaximum(distFilter.getMaxPointDistance());
108055         };
108056         BufferCurveMaximumDistanceFinder.prototype.findDistance = function findDistance (bufferCurve) {
108057           this.computeMaxVertexDistance(bufferCurve);
108058           this.computeMaxMidpointDistance(bufferCurve);
108059           return this._maxPtDist.getDistance()
108060         };
108061         BufferCurveMaximumDistanceFinder.prototype.getDistancePoints = function getDistancePoints () {
108062           return this._maxPtDist
108063         };
108064         BufferCurveMaximumDistanceFinder.prototype.interfaces_ = function interfaces_ () {
108065           return []
108066         };
108067         BufferCurveMaximumDistanceFinder.prototype.getClass = function getClass () {
108068           return BufferCurveMaximumDistanceFinder
108069         };
108070         staticAccessors$36.MaxPointDistanceFilter.get = function () { return MaxPointDistanceFilter };
108071         staticAccessors$36.MaxMidpointDistanceFilter.get = function () { return MaxMidpointDistanceFilter };
108072
108073         Object.defineProperties( BufferCurveMaximumDistanceFinder, staticAccessors$36 );
108074
108075         var MaxPointDistanceFilter = function MaxPointDistanceFilter (geom) {
108076           this._maxPtDist = new PointPairDistance();
108077           this._minPtDist = new PointPairDistance();
108078           this._geom = geom || null;
108079         };
108080         MaxPointDistanceFilter.prototype.filter = function filter (pt) {
108081           this._minPtDist.initialize();
108082           DistanceToPointFinder.computeDistance(this._geom, pt, this._minPtDist);
108083           this._maxPtDist.setMaximum(this._minPtDist);
108084         };
108085         MaxPointDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
108086           return this._maxPtDist
108087         };
108088         MaxPointDistanceFilter.prototype.interfaces_ = function interfaces_ () {
108089           return [CoordinateFilter]
108090         };
108091         MaxPointDistanceFilter.prototype.getClass = function getClass () {
108092           return MaxPointDistanceFilter
108093         };
108094
108095         var MaxMidpointDistanceFilter = function MaxMidpointDistanceFilter (geom) {
108096           this._maxPtDist = new PointPairDistance();
108097           this._minPtDist = new PointPairDistance();
108098           this._geom = geom || null;
108099         };
108100         MaxMidpointDistanceFilter.prototype.filter = function filter (seq, index) {
108101           if (index === 0) { return null }
108102           var p0 = seq.getCoordinate(index - 1);
108103           var p1 = seq.getCoordinate(index);
108104           var midPt = new Coordinate((p0.x + p1.x) / 2, (p0.y + p1.y) / 2);
108105           this._minPtDist.initialize();
108106           DistanceToPointFinder.computeDistance(this._geom, midPt, this._minPtDist);
108107           this._maxPtDist.setMaximum(this._minPtDist);
108108         };
108109         MaxMidpointDistanceFilter.prototype.isDone = function isDone () {
108110           return false
108111         };
108112         MaxMidpointDistanceFilter.prototype.isGeometryChanged = function isGeometryChanged () {
108113           return false
108114         };
108115         MaxMidpointDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
108116           return this._maxPtDist
108117         };
108118         MaxMidpointDistanceFilter.prototype.interfaces_ = function interfaces_ () {
108119           return [CoordinateSequenceFilter]
108120         };
108121         MaxMidpointDistanceFilter.prototype.getClass = function getClass () {
108122           return MaxMidpointDistanceFilter
108123         };
108124
108125         var PolygonExtracter = function PolygonExtracter (comps) {
108126           this._comps = comps || null;
108127         };
108128         PolygonExtracter.prototype.filter = function filter (geom) {
108129           if (geom instanceof Polygon) { this._comps.add(geom); }
108130         };
108131         PolygonExtracter.prototype.interfaces_ = function interfaces_ () {
108132           return [GeometryFilter]
108133         };
108134         PolygonExtracter.prototype.getClass = function getClass () {
108135           return PolygonExtracter
108136         };
108137         PolygonExtracter.getPolygons = function getPolygons () {
108138           if (arguments.length === 1) {
108139             var geom = arguments[0];
108140             return PolygonExtracter.getPolygons(geom, new ArrayList())
108141           } else if (arguments.length === 2) {
108142             var geom$1 = arguments[0];
108143             var list = arguments[1];
108144             if (geom$1 instanceof Polygon) {
108145               list.add(geom$1);
108146             } else if (geom$1 instanceof GeometryCollection) {
108147               geom$1.apply(new PolygonExtracter(list));
108148             }
108149             return list
108150           }
108151         };
108152
108153         var LinearComponentExtracter = function LinearComponentExtracter () {
108154           this._lines = null;
108155           this._isForcedToLineString = false;
108156           if (arguments.length === 1) {
108157             var lines = arguments[0];
108158             this._lines = lines;
108159           } else if (arguments.length === 2) {
108160             var lines$1 = arguments[0];
108161             var isForcedToLineString = arguments[1];
108162             this._lines = lines$1;
108163             this._isForcedToLineString = isForcedToLineString;
108164           }
108165         };
108166         LinearComponentExtracter.prototype.filter = function filter (geom) {
108167           if (this._isForcedToLineString && geom instanceof LinearRing) {
108168             var line = geom.getFactory().createLineString(geom.getCoordinateSequence());
108169             this._lines.add(line);
108170             return null
108171           }
108172           if (geom instanceof LineString) { this._lines.add(geom); }
108173         };
108174         LinearComponentExtracter.prototype.setForceToLineString = function setForceToLineString (isForcedToLineString) {
108175           this._isForcedToLineString = isForcedToLineString;
108176         };
108177         LinearComponentExtracter.prototype.interfaces_ = function interfaces_ () {
108178           return [GeometryComponentFilter]
108179         };
108180         LinearComponentExtracter.prototype.getClass = function getClass () {
108181           return LinearComponentExtracter
108182         };
108183         LinearComponentExtracter.getGeometry = function getGeometry () {
108184           if (arguments.length === 1) {
108185             var geom = arguments[0];
108186             return geom.getFactory().buildGeometry(LinearComponentExtracter.getLines(geom))
108187           } else if (arguments.length === 2) {
108188             var geom$1 = arguments[0];
108189             var forceToLineString = arguments[1];
108190             return geom$1.getFactory().buildGeometry(LinearComponentExtracter.getLines(geom$1, forceToLineString))
108191           }
108192         };
108193         LinearComponentExtracter.getLines = function getLines () {
108194           if (arguments.length === 1) {
108195             var geom = arguments[0];
108196             return LinearComponentExtracter.getLines(geom, false)
108197           } else if (arguments.length === 2) {
108198             if (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], Collection)) {
108199               var geoms = arguments[0];
108200               var lines$1 = arguments[1];
108201               for (var i = geoms.iterator(); i.hasNext();) {
108202                 var g = i.next();
108203                 LinearComponentExtracter.getLines(g, lines$1);
108204               }
108205               return lines$1
108206             } else if (arguments[0] instanceof Geometry && typeof arguments[1] === 'boolean') {
108207               var geom$1 = arguments[0];
108208               var forceToLineString = arguments[1];
108209               var lines = new ArrayList();
108210               geom$1.apply(new LinearComponentExtracter(lines, forceToLineString));
108211               return lines
108212             } else if (arguments[0] instanceof Geometry && hasInterface(arguments[1], Collection)) {
108213               var geom$2 = arguments[0];
108214               var lines$2 = arguments[1];
108215               if (geom$2 instanceof LineString) {
108216                 lines$2.add(geom$2);
108217               } else {
108218                 geom$2.apply(new LinearComponentExtracter(lines$2));
108219               }
108220               return lines$2
108221             }
108222           } else if (arguments.length === 3) {
108223             if (typeof arguments[2] === 'boolean' && (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], Collection))) {
108224               var geoms$1 = arguments[0];
108225               var lines$3 = arguments[1];
108226               var forceToLineString$1 = arguments[2];
108227               for (var i$1 = geoms$1.iterator(); i$1.hasNext();) {
108228                 var g$1 = i$1.next();
108229                 LinearComponentExtracter.getLines(g$1, lines$3, forceToLineString$1);
108230               }
108231               return lines$3
108232             } else if (typeof arguments[2] === 'boolean' && (arguments[0] instanceof Geometry && hasInterface(arguments[1], Collection))) {
108233               var geom$3 = arguments[0];
108234               var lines$4 = arguments[1];
108235               var forceToLineString$2 = arguments[2];
108236               geom$3.apply(new LinearComponentExtracter(lines$4, forceToLineString$2));
108237               return lines$4
108238             }
108239           }
108240         };
108241
108242         var PointLocator = function PointLocator () {
108243           this._boundaryRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
108244           this._isIn = null;
108245           this._numBoundaries = null;
108246           if (arguments.length === 0) ; else if (arguments.length === 1) {
108247             var boundaryRule = arguments[0];
108248             if (boundaryRule === null) { throw new IllegalArgumentException('Rule must be non-null') }
108249             this._boundaryRule = boundaryRule;
108250           }
108251         };
108252         PointLocator.prototype.locateInternal = function locateInternal () {
108253             var this$1 = this;
108254
108255           if (arguments[0] instanceof Coordinate && arguments[1] instanceof Polygon) {
108256             var p = arguments[0];
108257             var poly = arguments[1];
108258             if (poly.isEmpty()) { return Location.EXTERIOR }
108259             var shell = poly.getExteriorRing();
108260             var shellLoc = this.locateInPolygonRing(p, shell);
108261             if (shellLoc === Location.EXTERIOR) { return Location.EXTERIOR }
108262             if (shellLoc === Location.BOUNDARY) { return Location.BOUNDARY }
108263             for (var i = 0; i < poly.getNumInteriorRing(); i++) {
108264               var hole = poly.getInteriorRingN(i);
108265               var holeLoc = this$1.locateInPolygonRing(p, hole);
108266               if (holeLoc === Location.INTERIOR) { return Location.EXTERIOR }
108267               if (holeLoc === Location.BOUNDARY) { return Location.BOUNDARY }
108268             }
108269             return Location.INTERIOR
108270           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof LineString) {
108271             var p$1 = arguments[0];
108272             var l = arguments[1];
108273             if (!l.getEnvelopeInternal().intersects(p$1)) { return Location.EXTERIOR }
108274             var pt = l.getCoordinates();
108275             if (!l.isClosed()) {
108276               if (p$1.equals(pt[0]) || p$1.equals(pt[pt.length - 1])) {
108277                 return Location.BOUNDARY
108278               }
108279             }
108280             if (CGAlgorithms.isOnLine(p$1, pt)) { return Location.INTERIOR }
108281             return Location.EXTERIOR
108282           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Point$1) {
108283             var p$2 = arguments[0];
108284             var pt$1 = arguments[1];
108285             var ptCoord = pt$1.getCoordinate();
108286             if (ptCoord.equals2D(p$2)) { return Location.INTERIOR }
108287             return Location.EXTERIOR
108288           }
108289         };
108290         PointLocator.prototype.locateInPolygonRing = function locateInPolygonRing (p, ring) {
108291           if (!ring.getEnvelopeInternal().intersects(p)) { return Location.EXTERIOR }
108292           return CGAlgorithms.locatePointInRing(p, ring.getCoordinates())
108293         };
108294         PointLocator.prototype.intersects = function intersects (p, geom) {
108295           return this.locate(p, geom) !== Location.EXTERIOR
108296         };
108297         PointLocator.prototype.updateLocationInfo = function updateLocationInfo (loc) {
108298           if (loc === Location.INTERIOR) { this._isIn = true; }
108299           if (loc === Location.BOUNDARY) { this._numBoundaries++; }
108300         };
108301         PointLocator.prototype.computeLocation = function computeLocation (p, geom) {
108302             var this$1 = this;
108303
108304           if (geom instanceof Point$1) {
108305             this.updateLocationInfo(this.locateInternal(p, geom));
108306           }
108307           if (geom instanceof LineString) {
108308             this.updateLocationInfo(this.locateInternal(p, geom));
108309           } else if (geom instanceof Polygon) {
108310             this.updateLocationInfo(this.locateInternal(p, geom));
108311           } else if (geom instanceof MultiLineString) {
108312             var ml = geom;
108313             for (var i = 0; i < ml.getNumGeometries(); i++) {
108314               var l = ml.getGeometryN(i);
108315               this$1.updateLocationInfo(this$1.locateInternal(p, l));
108316             }
108317           } else if (geom instanceof MultiPolygon) {
108318             var mpoly = geom;
108319             for (var i$1 = 0; i$1 < mpoly.getNumGeometries(); i$1++) {
108320               var poly = mpoly.getGeometryN(i$1);
108321               this$1.updateLocationInfo(this$1.locateInternal(p, poly));
108322             }
108323           } else if (geom instanceof GeometryCollection) {
108324             var geomi = new GeometryCollectionIterator(geom);
108325             while (geomi.hasNext()) {
108326               var g2 = geomi.next();
108327               if (g2 !== geom) { this$1.computeLocation(p, g2); }
108328             }
108329           }
108330         };
108331         PointLocator.prototype.locate = function locate (p, geom) {
108332           if (geom.isEmpty()) { return Location.EXTERIOR }
108333           if (geom instanceof LineString) {
108334             return this.locateInternal(p, geom)
108335           } else if (geom instanceof Polygon) {
108336             return this.locateInternal(p, geom)
108337           }
108338           this._isIn = false;
108339           this._numBoundaries = 0;
108340           this.computeLocation(p, geom);
108341           if (this._boundaryRule.isInBoundary(this._numBoundaries)) { return Location.BOUNDARY }
108342           if (this._numBoundaries > 0 || this._isIn) { return Location.INTERIOR }
108343           return Location.EXTERIOR
108344         };
108345         PointLocator.prototype.interfaces_ = function interfaces_ () {
108346           return []
108347         };
108348         PointLocator.prototype.getClass = function getClass () {
108349           return PointLocator
108350         };
108351
108352         var GeometryLocation = function GeometryLocation () {
108353           this._component = null;
108354           this._segIndex = null;
108355           this._pt = null;
108356           if (arguments.length === 2) {
108357             var component = arguments[0];
108358             var pt = arguments[1];
108359             GeometryLocation.call(this, component, GeometryLocation.INSIDE_AREA, pt);
108360           } else if (arguments.length === 3) {
108361             var component$1 = arguments[0];
108362             var segIndex = arguments[1];
108363             var pt$1 = arguments[2];
108364             this._component = component$1;
108365             this._segIndex = segIndex;
108366             this._pt = pt$1;
108367           }
108368         };
108369
108370         var staticAccessors$38 = { INSIDE_AREA: { configurable: true } };
108371         GeometryLocation.prototype.isInsideArea = function isInsideArea () {
108372           return this._segIndex === GeometryLocation.INSIDE_AREA
108373         };
108374         GeometryLocation.prototype.getCoordinate = function getCoordinate () {
108375           return this._pt
108376         };
108377         GeometryLocation.prototype.getGeometryComponent = function getGeometryComponent () {
108378           return this._component
108379         };
108380         GeometryLocation.prototype.getSegmentIndex = function getSegmentIndex () {
108381           return this._segIndex
108382         };
108383         GeometryLocation.prototype.interfaces_ = function interfaces_ () {
108384           return []
108385         };
108386         GeometryLocation.prototype.getClass = function getClass () {
108387           return GeometryLocation
108388         };
108389         staticAccessors$38.INSIDE_AREA.get = function () { return -1 };
108390
108391         Object.defineProperties( GeometryLocation, staticAccessors$38 );
108392
108393         var PointExtracter = function PointExtracter (pts) {
108394           this._pts = pts || null;
108395         };
108396         PointExtracter.prototype.filter = function filter (geom) {
108397           if (geom instanceof Point$1) { this._pts.add(geom); }
108398         };
108399         PointExtracter.prototype.interfaces_ = function interfaces_ () {
108400           return [GeometryFilter]
108401         };
108402         PointExtracter.prototype.getClass = function getClass () {
108403           return PointExtracter
108404         };
108405         PointExtracter.getPoints = function getPoints () {
108406           if (arguments.length === 1) {
108407             var geom = arguments[0];
108408             if (geom instanceof Point$1) {
108409               return Collections.singletonList(geom)
108410             }
108411             return PointExtracter.getPoints(geom, new ArrayList())
108412           } else if (arguments.length === 2) {
108413             var geom$1 = arguments[0];
108414             var list = arguments[1];
108415             if (geom$1 instanceof Point$1) {
108416               list.add(geom$1);
108417             } else if (geom$1 instanceof GeometryCollection) {
108418               geom$1.apply(new PointExtracter(list));
108419             }
108420             return list
108421           }
108422         };
108423
108424         var ConnectedElementLocationFilter = function ConnectedElementLocationFilter () {
108425           this._locations = null;
108426           var locations = arguments[0];
108427           this._locations = locations;
108428         };
108429         ConnectedElementLocationFilter.prototype.filter = function filter (geom) {
108430           if (geom instanceof Point$1 || geom instanceof LineString || geom instanceof Polygon) { this._locations.add(new GeometryLocation(geom, 0, geom.getCoordinate())); }
108431         };
108432         ConnectedElementLocationFilter.prototype.interfaces_ = function interfaces_ () {
108433           return [GeometryFilter]
108434         };
108435         ConnectedElementLocationFilter.prototype.getClass = function getClass () {
108436           return ConnectedElementLocationFilter
108437         };
108438         ConnectedElementLocationFilter.getLocations = function getLocations (geom) {
108439           var locations = new ArrayList();
108440           geom.apply(new ConnectedElementLocationFilter(locations));
108441           return locations
108442         };
108443
108444         var DistanceOp = function DistanceOp () {
108445           this._geom = null;
108446           this._terminateDistance = 0.0;
108447           this._ptLocator = new PointLocator();
108448           this._minDistanceLocation = null;
108449           this._minDistance = Double.MAX_VALUE;
108450           if (arguments.length === 2) {
108451             var g0 = arguments[0];
108452             var g1 = arguments[1];
108453             this._geom = [g0, g1];
108454             this._terminateDistance = 0.0;
108455           } else if (arguments.length === 3) {
108456             var g0$1 = arguments[0];
108457             var g1$1 = arguments[1];
108458             var terminateDistance = arguments[2];
108459             this._geom = new Array(2).fill(null);
108460             this._geom[0] = g0$1;
108461             this._geom[1] = g1$1;
108462             this._terminateDistance = terminateDistance;
108463           }
108464         };
108465         DistanceOp.prototype.computeContainmentDistance = function computeContainmentDistance () {
108466             var this$1 = this;
108467
108468           if (arguments.length === 0) {
108469             var locPtPoly = new Array(2).fill(null);
108470             this.computeContainmentDistance(0, locPtPoly);
108471             if (this._minDistance <= this._terminateDistance) { return null }
108472             this.computeContainmentDistance(1, locPtPoly);
108473           } else if (arguments.length === 2) {
108474             var polyGeomIndex = arguments[0];
108475             var locPtPoly$1 = arguments[1];
108476             var locationsIndex = 1 - polyGeomIndex;
108477             var polys = PolygonExtracter.getPolygons(this._geom[polyGeomIndex]);
108478             if (polys.size() > 0) {
108479               var insideLocs = ConnectedElementLocationFilter.getLocations(this._geom[locationsIndex]);
108480               this.computeContainmentDistance(insideLocs, polys, locPtPoly$1);
108481               if (this._minDistance <= this._terminateDistance) {
108482                 this._minDistanceLocation[locationsIndex] = locPtPoly$1[0];
108483                 this._minDistanceLocation[polyGeomIndex] = locPtPoly$1[1];
108484                 return null
108485               }
108486             }
108487           } else if (arguments.length === 3) {
108488             if (arguments[2] instanceof Array && (hasInterface(arguments[0], List) && hasInterface(arguments[1], List))) {
108489               var locs = arguments[0];
108490               var polys$1 = arguments[1];
108491               var locPtPoly$2 = arguments[2];
108492               for (var i = 0; i < locs.size(); i++) {
108493                 var loc = locs.get(i);
108494                 for (var j = 0; j < polys$1.size(); j++) {
108495                   this$1.computeContainmentDistance(loc, polys$1.get(j), locPtPoly$2);
108496                   if (this$1._minDistance <= this$1._terminateDistance) { return null }
108497                 }
108498               }
108499             } else if (arguments[2] instanceof Array && (arguments[0] instanceof GeometryLocation && arguments[1] instanceof Polygon)) {
108500               var ptLoc = arguments[0];
108501               var poly = arguments[1];
108502               var locPtPoly$3 = arguments[2];
108503               var pt = ptLoc.getCoordinate();
108504               if (Location.EXTERIOR !== this._ptLocator.locate(pt, poly)) {
108505                 this._minDistance = 0.0;
108506                 locPtPoly$3[0] = ptLoc;
108507                 locPtPoly$3[1] = new GeometryLocation(poly, pt);
108508
108509                 return null
108510               }
108511             }
108512           }
108513         };
108514         DistanceOp.prototype.computeMinDistanceLinesPoints = function computeMinDistanceLinesPoints (lines, points, locGeom) {
108515             var this$1 = this;
108516
108517           for (var i = 0; i < lines.size(); i++) {
108518             var line = lines.get(i);
108519             for (var j = 0; j < points.size(); j++) {
108520               var pt = points.get(j);
108521               this$1.computeMinDistance(line, pt, locGeom);
108522               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108523             }
108524           }
108525         };
108526         DistanceOp.prototype.computeFacetDistance = function computeFacetDistance () {
108527           var locGeom = new Array(2).fill(null);
108528           var lines0 = LinearComponentExtracter.getLines(this._geom[0]);
108529           var lines1 = LinearComponentExtracter.getLines(this._geom[1]);
108530           var pts0 = PointExtracter.getPoints(this._geom[0]);
108531           var pts1 = PointExtracter.getPoints(this._geom[1]);
108532           this.computeMinDistanceLines(lines0, lines1, locGeom);
108533           this.updateMinDistance(locGeom, false);
108534           if (this._minDistance <= this._terminateDistance) { return null }
108535           locGeom[0] = null;
108536           locGeom[1] = null;
108537           this.computeMinDistanceLinesPoints(lines0, pts1, locGeom);
108538           this.updateMinDistance(locGeom, false);
108539           if (this._minDistance <= this._terminateDistance) { return null }
108540           locGeom[0] = null;
108541           locGeom[1] = null;
108542           this.computeMinDistanceLinesPoints(lines1, pts0, locGeom);
108543           this.updateMinDistance(locGeom, true);
108544           if (this._minDistance <= this._terminateDistance) { return null }
108545           locGeom[0] = null;
108546           locGeom[1] = null;
108547           this.computeMinDistancePoints(pts0, pts1, locGeom);
108548           this.updateMinDistance(locGeom, false);
108549         };
108550         DistanceOp.prototype.nearestLocations = function nearestLocations () {
108551           this.computeMinDistance();
108552           return this._minDistanceLocation
108553         };
108554         DistanceOp.prototype.updateMinDistance = function updateMinDistance (locGeom, flip) {
108555           if (locGeom[0] === null) { return null }
108556           if (flip) {
108557             this._minDistanceLocation[0] = locGeom[1];
108558             this._minDistanceLocation[1] = locGeom[0];
108559           } else {
108560             this._minDistanceLocation[0] = locGeom[0];
108561             this._minDistanceLocation[1] = locGeom[1];
108562           }
108563         };
108564         DistanceOp.prototype.nearestPoints = function nearestPoints () {
108565           this.computeMinDistance();
108566           var nearestPts = [this._minDistanceLocation[0].getCoordinate(), this._minDistanceLocation[1].getCoordinate()];
108567           return nearestPts
108568         };
108569         DistanceOp.prototype.computeMinDistance = function computeMinDistance () {
108570             var this$1 = this;
108571
108572           if (arguments.length === 0) {
108573             if (this._minDistanceLocation !== null) { return null }
108574             this._minDistanceLocation = new Array(2).fill(null);
108575             this.computeContainmentDistance();
108576             if (this._minDistance <= this._terminateDistance) { return null }
108577             this.computeFacetDistance();
108578           } else if (arguments.length === 3) {
108579             if (arguments[2] instanceof Array && (arguments[0] instanceof LineString && arguments[1] instanceof Point$1)) {
108580               var line = arguments[0];
108581               var pt = arguments[1];
108582               var locGeom = arguments[2];
108583               if (line.getEnvelopeInternal().distance(pt.getEnvelopeInternal()) > this._minDistance) { return null }
108584               var coord0 = line.getCoordinates();
108585               var coord = pt.getCoordinate();
108586               for (var i = 0; i < coord0.length - 1; i++) {
108587                 var dist = CGAlgorithms.distancePointLine(coord, coord0[i], coord0[i + 1]);
108588                 if (dist < this$1._minDistance) {
108589                   this$1._minDistance = dist;
108590                   var seg = new LineSegment(coord0[i], coord0[i + 1]);
108591                   var segClosestPoint = seg.closestPoint(coord);
108592                   locGeom[0] = new GeometryLocation(line, i, segClosestPoint);
108593                   locGeom[1] = new GeometryLocation(pt, 0, coord);
108594                 }
108595                 if (this$1._minDistance <= this$1._terminateDistance) { return null }
108596               }
108597             } else if (arguments[2] instanceof Array && (arguments[0] instanceof LineString && arguments[1] instanceof LineString)) {
108598               var line0 = arguments[0];
108599               var line1 = arguments[1];
108600               var locGeom$1 = arguments[2];
108601               if (line0.getEnvelopeInternal().distance(line1.getEnvelopeInternal()) > this._minDistance) { return null }
108602               var coord0$1 = line0.getCoordinates();
108603               var coord1 = line1.getCoordinates();
108604               for (var i$1 = 0; i$1 < coord0$1.length - 1; i$1++) {
108605                 for (var j = 0; j < coord1.length - 1; j++) {
108606                   var dist$1 = CGAlgorithms.distanceLineLine(coord0$1[i$1], coord0$1[i$1 + 1], coord1[j], coord1[j + 1]);
108607                   if (dist$1 < this$1._minDistance) {
108608                     this$1._minDistance = dist$1;
108609                     var seg0 = new LineSegment(coord0$1[i$1], coord0$1[i$1 + 1]);
108610                     var seg1 = new LineSegment(coord1[j], coord1[j + 1]);
108611                     var closestPt = seg0.closestPoints(seg1);
108612                     locGeom$1[0] = new GeometryLocation(line0, i$1, closestPt[0]);
108613                     locGeom$1[1] = new GeometryLocation(line1, j, closestPt[1]);
108614                   }
108615                   if (this$1._minDistance <= this$1._terminateDistance) { return null }
108616                 }
108617               }
108618             }
108619           }
108620         };
108621         DistanceOp.prototype.computeMinDistancePoints = function computeMinDistancePoints (points0, points1, locGeom) {
108622             var this$1 = this;
108623
108624           for (var i = 0; i < points0.size(); i++) {
108625             var pt0 = points0.get(i);
108626             for (var j = 0; j < points1.size(); j++) {
108627               var pt1 = points1.get(j);
108628               var dist = pt0.getCoordinate().distance(pt1.getCoordinate());
108629               if (dist < this$1._minDistance) {
108630                 this$1._minDistance = dist;
108631                 locGeom[0] = new GeometryLocation(pt0, 0, pt0.getCoordinate());
108632                 locGeom[1] = new GeometryLocation(pt1, 0, pt1.getCoordinate());
108633               }
108634               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108635             }
108636           }
108637         };
108638         DistanceOp.prototype.distance = function distance () {
108639           if (this._geom[0] === null || this._geom[1] === null) { throw new IllegalArgumentException('null geometries are not supported') }
108640           if (this._geom[0].isEmpty() || this._geom[1].isEmpty()) { return 0.0 }
108641           this.computeMinDistance();
108642           return this._minDistance
108643         };
108644         DistanceOp.prototype.computeMinDistanceLines = function computeMinDistanceLines (lines0, lines1, locGeom) {
108645             var this$1 = this;
108646
108647           for (var i = 0; i < lines0.size(); i++) {
108648             var line0 = lines0.get(i);
108649             for (var j = 0; j < lines1.size(); j++) {
108650               var line1 = lines1.get(j);
108651               this$1.computeMinDistance(line0, line1, locGeom);
108652               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108653             }
108654           }
108655         };
108656         DistanceOp.prototype.interfaces_ = function interfaces_ () {
108657           return []
108658         };
108659         DistanceOp.prototype.getClass = function getClass () {
108660           return DistanceOp
108661         };
108662         DistanceOp.distance = function distance (g0, g1) {
108663           var distOp = new DistanceOp(g0, g1);
108664           return distOp.distance()
108665         };
108666         DistanceOp.isWithinDistance = function isWithinDistance (g0, g1, distance) {
108667           var distOp = new DistanceOp(g0, g1, distance);
108668           return distOp.distance() <= distance
108669         };
108670         DistanceOp.nearestPoints = function nearestPoints (g0, g1) {
108671           var distOp = new DistanceOp(g0, g1);
108672           return distOp.nearestPoints()
108673         };
108674
108675         var PointPairDistance$2 = function PointPairDistance () {
108676           this._pt = [new Coordinate(), new Coordinate()];
108677           this._distance = Double.NaN;
108678           this._isNull = true;
108679         };
108680         PointPairDistance$2.prototype.getCoordinates = function getCoordinates () {
108681           return this._pt
108682         };
108683         PointPairDistance$2.prototype.getCoordinate = function getCoordinate (i) {
108684           return this._pt[i]
108685         };
108686         PointPairDistance$2.prototype.setMinimum = function setMinimum () {
108687           if (arguments.length === 1) {
108688             var ptDist = arguments[0];
108689             this.setMinimum(ptDist._pt[0], ptDist._pt[1]);
108690           } else if (arguments.length === 2) {
108691             var p0 = arguments[0];
108692             var p1 = arguments[1];
108693             if (this._isNull) {
108694               this.initialize(p0, p1);
108695               return null
108696             }
108697             var dist = p0.distance(p1);
108698             if (dist < this._distance) { this.initialize(p0, p1, dist); }
108699           }
108700         };
108701         PointPairDistance$2.prototype.initialize = function initialize () {
108702           if (arguments.length === 0) {
108703             this._isNull = true;
108704           } else if (arguments.length === 2) {
108705             var p0 = arguments[0];
108706             var p1 = arguments[1];
108707             this._pt[0].setCoordinate(p0);
108708             this._pt[1].setCoordinate(p1);
108709             this._distance = p0.distance(p1);
108710             this._isNull = false;
108711           } else if (arguments.length === 3) {
108712             var p0$1 = arguments[0];
108713             var p1$1 = arguments[1];
108714             var distance = arguments[2];
108715             this._pt[0].setCoordinate(p0$1);
108716             this._pt[1].setCoordinate(p1$1);
108717             this._distance = distance;
108718             this._isNull = false;
108719           }
108720         };
108721         PointPairDistance$2.prototype.toString = function toString () {
108722           return WKTWriter.toLineString(this._pt[0], this._pt[1])
108723         };
108724         PointPairDistance$2.prototype.getDistance = function getDistance () {
108725           return this._distance
108726         };
108727         PointPairDistance$2.prototype.setMaximum = function setMaximum () {
108728           if (arguments.length === 1) {
108729             var ptDist = arguments[0];
108730             this.setMaximum(ptDist._pt[0], ptDist._pt[1]);
108731           } else if (arguments.length === 2) {
108732             var p0 = arguments[0];
108733             var p1 = arguments[1];
108734             if (this._isNull) {
108735               this.initialize(p0, p1);
108736               return null
108737             }
108738             var dist = p0.distance(p1);
108739             if (dist > this._distance) { this.initialize(p0, p1, dist); }
108740           }
108741         };
108742         PointPairDistance$2.prototype.interfaces_ = function interfaces_ () {
108743           return []
108744         };
108745         PointPairDistance$2.prototype.getClass = function getClass () {
108746           return PointPairDistance$2
108747         };
108748
108749         var DistanceToPoint = function DistanceToPoint () {};
108750
108751         DistanceToPoint.prototype.interfaces_ = function interfaces_ () {
108752           return []
108753         };
108754         DistanceToPoint.prototype.getClass = function getClass () {
108755           return DistanceToPoint
108756         };
108757         DistanceToPoint.computeDistance = function computeDistance () {
108758           if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof LineString && arguments[1] instanceof Coordinate)) {
108759             var line = arguments[0];
108760             var pt = arguments[1];
108761             var ptDist = arguments[2];
108762             var tempSegment = new LineSegment();
108763             var coords = line.getCoordinates();
108764             for (var i = 0; i < coords.length - 1; i++) {
108765               tempSegment.setCoordinates(coords[i], coords[i + 1]);
108766               var closestPt = tempSegment.closestPoint(pt);
108767               ptDist.setMinimum(closestPt, pt);
108768             }
108769           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof Polygon && arguments[1] instanceof Coordinate)) {
108770             var poly = arguments[0];
108771             var pt$1 = arguments[1];
108772             var ptDist$1 = arguments[2];
108773             DistanceToPoint.computeDistance(poly.getExteriorRing(), pt$1, ptDist$1);
108774             for (var i$1 = 0; i$1 < poly.getNumInteriorRing(); i$1++) {
108775               DistanceToPoint.computeDistance(poly.getInteriorRingN(i$1), pt$1, ptDist$1);
108776             }
108777           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof Geometry && arguments[1] instanceof Coordinate)) {
108778             var geom = arguments[0];
108779             var pt$2 = arguments[1];
108780             var ptDist$2 = arguments[2];
108781             if (geom instanceof LineString) {
108782               DistanceToPoint.computeDistance(geom, pt$2, ptDist$2);
108783             } else if (geom instanceof Polygon) {
108784               DistanceToPoint.computeDistance(geom, pt$2, ptDist$2);
108785             } else if (geom instanceof GeometryCollection) {
108786               var gc = geom;
108787               for (var i$2 = 0; i$2 < gc.getNumGeometries(); i$2++) {
108788                 var g = gc.getGeometryN(i$2);
108789                 DistanceToPoint.computeDistance(g, pt$2, ptDist$2);
108790               }
108791             } else {
108792               ptDist$2.setMinimum(geom.getCoordinate(), pt$2);
108793             }
108794           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof LineSegment && arguments[1] instanceof Coordinate)) {
108795             var segment = arguments[0];
108796             var pt$3 = arguments[1];
108797             var ptDist$3 = arguments[2];
108798             var closestPt$1 = segment.closestPoint(pt$3);
108799             ptDist$3.setMinimum(closestPt$1, pt$3);
108800           }
108801         };
108802
108803         var DiscreteHausdorffDistance = function DiscreteHausdorffDistance () {
108804           this._g0 = null;
108805           this._g1 = null;
108806           this._ptDist = new PointPairDistance$2();
108807           this._densifyFrac = 0.0;
108808           var g0 = arguments[0];
108809           var g1 = arguments[1];
108810           this._g0 = g0;
108811           this._g1 = g1;
108812         };
108813
108814         var staticAccessors$39 = { MaxPointDistanceFilter: { configurable: true },MaxDensifiedByFractionDistanceFilter: { configurable: true } };
108815         DiscreteHausdorffDistance.prototype.getCoordinates = function getCoordinates () {
108816           return this._ptDist.getCoordinates()
108817         };
108818         DiscreteHausdorffDistance.prototype.setDensifyFraction = function setDensifyFraction (densifyFrac) {
108819           if (densifyFrac > 1.0 || densifyFrac <= 0.0) { throw new IllegalArgumentException('Fraction is not in range (0.0 - 1.0]') }
108820           this._densifyFrac = densifyFrac;
108821         };
108822         DiscreteHausdorffDistance.prototype.compute = function compute (g0, g1) {
108823           this.computeOrientedDistance(g0, g1, this._ptDist);
108824           this.computeOrientedDistance(g1, g0, this._ptDist);
108825         };
108826         DiscreteHausdorffDistance.prototype.distance = function distance () {
108827           this.compute(this._g0, this._g1);
108828           return this._ptDist.getDistance()
108829         };
108830         DiscreteHausdorffDistance.prototype.computeOrientedDistance = function computeOrientedDistance (discreteGeom, geom, ptDist) {
108831           var distFilter = new MaxPointDistanceFilter$1(geom);
108832           discreteGeom.apply(distFilter);
108833           ptDist.setMaximum(distFilter.getMaxPointDistance());
108834           if (this._densifyFrac > 0) {
108835             var fracFilter = new MaxDensifiedByFractionDistanceFilter(geom, this._densifyFrac);
108836             discreteGeom.apply(fracFilter);
108837             ptDist.setMaximum(fracFilter.getMaxPointDistance());
108838           }
108839         };
108840         DiscreteHausdorffDistance.prototype.orientedDistance = function orientedDistance () {
108841           this.computeOrientedDistance(this._g0, this._g1, this._ptDist);
108842           return this._ptDist.getDistance()
108843         };
108844         DiscreteHausdorffDistance.prototype.interfaces_ = function interfaces_ () {
108845           return []
108846         };
108847         DiscreteHausdorffDistance.prototype.getClass = function getClass () {
108848           return DiscreteHausdorffDistance
108849         };
108850         DiscreteHausdorffDistance.distance = function distance () {
108851           if (arguments.length === 2) {
108852             var g0 = arguments[0];
108853             var g1 = arguments[1];
108854             var dist = new DiscreteHausdorffDistance(g0, g1);
108855             return dist.distance()
108856           } else if (arguments.length === 3) {
108857             var g0$1 = arguments[0];
108858             var g1$1 = arguments[1];
108859             var densifyFrac = arguments[2];
108860             var dist$1 = new DiscreteHausdorffDistance(g0$1, g1$1);
108861             dist$1.setDensifyFraction(densifyFrac);
108862             return dist$1.distance()
108863           }
108864         };
108865         staticAccessors$39.MaxPointDistanceFilter.get = function () { return MaxPointDistanceFilter$1 };
108866         staticAccessors$39.MaxDensifiedByFractionDistanceFilter.get = function () { return MaxDensifiedByFractionDistanceFilter };
108867
108868         Object.defineProperties( DiscreteHausdorffDistance, staticAccessors$39 );
108869
108870         var MaxPointDistanceFilter$1 = function MaxPointDistanceFilter () {
108871           this._maxPtDist = new PointPairDistance$2();
108872           this._minPtDist = new PointPairDistance$2();
108873           this._euclideanDist = new DistanceToPoint();
108874           this._geom = null;
108875           var geom = arguments[0];
108876           this._geom = geom;
108877         };
108878         MaxPointDistanceFilter$1.prototype.filter = function filter (pt) {
108879           this._minPtDist.initialize();
108880           DistanceToPoint.computeDistance(this._geom, pt, this._minPtDist);
108881           this._maxPtDist.setMaximum(this._minPtDist);
108882         };
108883         MaxPointDistanceFilter$1.prototype.getMaxPointDistance = function getMaxPointDistance () {
108884           return this._maxPtDist
108885         };
108886         MaxPointDistanceFilter$1.prototype.interfaces_ = function interfaces_ () {
108887           return [CoordinateFilter]
108888         };
108889         MaxPointDistanceFilter$1.prototype.getClass = function getClass () {
108890           return MaxPointDistanceFilter$1
108891         };
108892
108893         var MaxDensifiedByFractionDistanceFilter = function MaxDensifiedByFractionDistanceFilter () {
108894           this._maxPtDist = new PointPairDistance$2();
108895           this._minPtDist = new PointPairDistance$2();
108896           this._geom = null;
108897           this._numSubSegs = 0;
108898           var geom = arguments[0];
108899           var fraction = arguments[1];
108900           this._geom = geom;
108901           this._numSubSegs = Math.trunc(Math.round(1.0 / fraction));
108902         };
108903         MaxDensifiedByFractionDistanceFilter.prototype.filter = function filter (seq, index) {
108904             var this$1 = this;
108905
108906           if (index === 0) { return null }
108907           var p0 = seq.getCoordinate(index - 1);
108908           var p1 = seq.getCoordinate(index);
108909           var delx = (p1.x - p0.x) / this._numSubSegs;
108910           var dely = (p1.y - p0.y) / this._numSubSegs;
108911           for (var i = 0; i < this._numSubSegs; i++) {
108912             var x = p0.x + i * delx;
108913             var y = p0.y + i * dely;
108914             var pt = new Coordinate(x, y);
108915             this$1._minPtDist.initialize();
108916             DistanceToPoint.computeDistance(this$1._geom, pt, this$1._minPtDist);
108917             this$1._maxPtDist.setMaximum(this$1._minPtDist);
108918           }
108919         };
108920         MaxDensifiedByFractionDistanceFilter.prototype.isDone = function isDone () {
108921           return false
108922         };
108923         MaxDensifiedByFractionDistanceFilter.prototype.isGeometryChanged = function isGeometryChanged () {
108924           return false
108925         };
108926         MaxDensifiedByFractionDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
108927           return this._maxPtDist
108928         };
108929         MaxDensifiedByFractionDistanceFilter.prototype.interfaces_ = function interfaces_ () {
108930           return [CoordinateSequenceFilter]
108931         };
108932         MaxDensifiedByFractionDistanceFilter.prototype.getClass = function getClass () {
108933           return MaxDensifiedByFractionDistanceFilter
108934         };
108935
108936         var BufferDistanceValidator = function BufferDistanceValidator (input, bufDistance, result) {
108937           this._minValidDistance = null;
108938           this._maxValidDistance = null;
108939           this._minDistanceFound = null;
108940           this._maxDistanceFound = null;
108941           this._isValid = true;
108942           this._errMsg = null;
108943           this._errorLocation = null;
108944           this._errorIndicator = null;
108945           this._input = input || null;
108946           this._bufDistance = bufDistance || null;
108947           this._result = result || null;
108948         };
108949
108950         var staticAccessors$37 = { VERBOSE: { configurable: true },MAX_DISTANCE_DIFF_FRAC: { configurable: true } };
108951         BufferDistanceValidator.prototype.checkMaximumDistance = function checkMaximumDistance (input, bufCurve, maxDist) {
108952           var haus = new DiscreteHausdorffDistance(bufCurve, input);
108953           haus.setDensifyFraction(0.25);
108954           this._maxDistanceFound = haus.orientedDistance();
108955           if (this._maxDistanceFound > maxDist) {
108956             this._isValid = false;
108957             var pts = haus.getCoordinates();
108958             this._errorLocation = pts[1];
108959             this._errorIndicator = input.getFactory().createLineString(pts);
108960             this._errMsg = 'Distance between buffer curve and input is too large (' + this._maxDistanceFound + ' at ' + WKTWriter.toLineString(pts[0], pts[1]) + ')';
108961           }
108962         };
108963         BufferDistanceValidator.prototype.isValid = function isValid () {
108964           var posDistance = Math.abs(this._bufDistance);
108965           var distDelta = BufferDistanceValidator.MAX_DISTANCE_DIFF_FRAC * posDistance;
108966           this._minValidDistance = posDistance - distDelta;
108967           this._maxValidDistance = posDistance + distDelta;
108968           if (this._input.isEmpty() || this._result.isEmpty()) { return true }
108969           if (this._bufDistance > 0.0) {
108970             this.checkPositiveValid();
108971           } else {
108972             this.checkNegativeValid();
108973           }
108974           if (BufferDistanceValidator.VERBOSE) {
108975             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));
108976           }
108977           return this._isValid
108978         };
108979         BufferDistanceValidator.prototype.checkNegativeValid = function checkNegativeValid () {
108980           if (!(this._input instanceof Polygon || this._input instanceof MultiPolygon || this._input instanceof GeometryCollection)) {
108981             return null
108982           }
108983           var inputCurve = this.getPolygonLines(this._input);
108984           this.checkMinimumDistance(inputCurve, this._result, this._minValidDistance);
108985           if (!this._isValid) { return null }
108986           this.checkMaximumDistance(inputCurve, this._result, this._maxValidDistance);
108987         };
108988         BufferDistanceValidator.prototype.getErrorIndicator = function getErrorIndicator () {
108989           return this._errorIndicator
108990         };
108991         BufferDistanceValidator.prototype.checkMinimumDistance = function checkMinimumDistance (g1, g2, minDist) {
108992           var distOp = new DistanceOp(g1, g2, minDist);
108993           this._minDistanceFound = distOp.distance();
108994           if (this._minDistanceFound < minDist) {
108995             this._isValid = false;
108996             var pts = distOp.nearestPoints();
108997             this._errorLocation = distOp.nearestPoints()[1];
108998             this._errorIndicator = g1.getFactory().createLineString(pts);
108999             this._errMsg = 'Distance between buffer curve and input is too small (' + this._minDistanceFound + ' at ' + WKTWriter.toLineString(pts[0], pts[1]) + ' )';
109000           }
109001         };
109002         BufferDistanceValidator.prototype.checkPositiveValid = function checkPositiveValid () {
109003           var bufCurve = this._result.getBoundary();
109004           this.checkMinimumDistance(this._input, bufCurve, this._minValidDistance);
109005           if (!this._isValid) { return null }
109006           this.checkMaximumDistance(this._input, bufCurve, this._maxValidDistance);
109007         };
109008         BufferDistanceValidator.prototype.getErrorLocation = function getErrorLocation () {
109009           return this._errorLocation
109010         };
109011         BufferDistanceValidator.prototype.getPolygonLines = function getPolygonLines (g) {
109012           var lines = new ArrayList();
109013           var lineExtracter = new LinearComponentExtracter(lines);
109014           var polys = PolygonExtracter.getPolygons(g);
109015           for (var i = polys.iterator(); i.hasNext();) {
109016             var poly = i.next();
109017             poly.apply(lineExtracter);
109018           }
109019           return g.getFactory().buildGeometry(lines)
109020         };
109021         BufferDistanceValidator.prototype.getErrorMessage = function getErrorMessage () {
109022           return this._errMsg
109023         };
109024         BufferDistanceValidator.prototype.interfaces_ = function interfaces_ () {
109025           return []
109026         };
109027         BufferDistanceValidator.prototype.getClass = function getClass () {
109028           return BufferDistanceValidator
109029         };
109030         staticAccessors$37.VERBOSE.get = function () { return false };
109031         staticAccessors$37.MAX_DISTANCE_DIFF_FRAC.get = function () { return 0.012 };
109032
109033         Object.defineProperties( BufferDistanceValidator, staticAccessors$37 );
109034
109035         var BufferResultValidator = function BufferResultValidator (input, distance, result) {
109036           this._isValid = true;
109037           this._errorMsg = null;
109038           this._errorLocation = null;
109039           this._errorIndicator = null;
109040           this._input = input || null;
109041           this._distance = distance || null;
109042           this._result = result || null;
109043         };
109044
109045         var staticAccessors$40 = { VERBOSE: { configurable: true },MAX_ENV_DIFF_FRAC: { configurable: true } };
109046         BufferResultValidator.prototype.isValid = function isValid () {
109047           this.checkPolygonal();
109048           if (!this._isValid) { return this._isValid }
109049           this.checkExpectedEmpty();
109050           if (!this._isValid) { return this._isValid }
109051           this.checkEnvelope();
109052           if (!this._isValid) { return this._isValid }
109053           this.checkArea();
109054           if (!this._isValid) { return this._isValid }
109055           this.checkDistance();
109056           return this._isValid
109057         };
109058         BufferResultValidator.prototype.checkEnvelope = function checkEnvelope () {
109059           if (this._distance < 0.0) { return null }
109060           var padding = this._distance * BufferResultValidator.MAX_ENV_DIFF_FRAC;
109061           if (padding === 0.0) { padding = 0.001; }
109062           var expectedEnv = new Envelope(this._input.getEnvelopeInternal());
109063           expectedEnv.expandBy(this._distance);
109064           var bufEnv = new Envelope(this._result.getEnvelopeInternal());
109065           bufEnv.expandBy(padding);
109066           if (!bufEnv.contains(expectedEnv)) {
109067             this._isValid = false;
109068             this._errorMsg = 'Buffer envelope is incorrect';
109069             this._errorIndicator = this._input.getFactory().toGeometry(bufEnv);
109070           }
109071           this.report('Envelope');
109072         };
109073         BufferResultValidator.prototype.checkDistance = function checkDistance () {
109074           var distValid = new BufferDistanceValidator(this._input, this._distance, this._result);
109075           if (!distValid.isValid()) {
109076             this._isValid = false;
109077             this._errorMsg = distValid.getErrorMessage();
109078             this._errorLocation = distValid.getErrorLocation();
109079             this._errorIndicator = distValid.getErrorIndicator();
109080           }
109081           this.report('Distance');
109082         };
109083         BufferResultValidator.prototype.checkArea = function checkArea () {
109084           var inputArea = this._input.getArea();
109085           var resultArea = this._result.getArea();
109086           if (this._distance > 0.0 && inputArea > resultArea) {
109087             this._isValid = false;
109088             this._errorMsg = 'Area of positive buffer is smaller than input';
109089             this._errorIndicator = this._result;
109090           }
109091           if (this._distance < 0.0 && inputArea < resultArea) {
109092             this._isValid = false;
109093             this._errorMsg = 'Area of negative buffer is larger than input';
109094             this._errorIndicator = this._result;
109095           }
109096           this.report('Area');
109097         };
109098         BufferResultValidator.prototype.checkPolygonal = function checkPolygonal () {
109099           if (!(this._result instanceof Polygon || this._result instanceof MultiPolygon)) { this._isValid = false; }
109100           this._errorMsg = 'Result is not polygonal';
109101           this._errorIndicator = this._result;
109102           this.report('Polygonal');
109103         };
109104         BufferResultValidator.prototype.getErrorIndicator = function getErrorIndicator () {
109105           return this._errorIndicator
109106         };
109107         BufferResultValidator.prototype.getErrorLocation = function getErrorLocation () {
109108           return this._errorLocation
109109         };
109110         BufferResultValidator.prototype.checkExpectedEmpty = function checkExpectedEmpty () {
109111           if (this._input.getDimension() >= 2) { return null }
109112           if (this._distance > 0.0) { return null }
109113           if (!this._result.isEmpty()) {
109114             this._isValid = false;
109115             this._errorMsg = 'Result is non-empty';
109116             this._errorIndicator = this._result;
109117           }
109118           this.report('ExpectedEmpty');
109119         };
109120         BufferResultValidator.prototype.report = function report (checkName) {
109121           if (!BufferResultValidator.VERBOSE) { return null }
109122           System.out.println('Check ' + checkName + ': ' + (this._isValid ? 'passed' : 'FAILED'));
109123         };
109124         BufferResultValidator.prototype.getErrorMessage = function getErrorMessage () {
109125           return this._errorMsg
109126         };
109127         BufferResultValidator.prototype.interfaces_ = function interfaces_ () {
109128           return []
109129         };
109130         BufferResultValidator.prototype.getClass = function getClass () {
109131           return BufferResultValidator
109132         };
109133         BufferResultValidator.isValidMsg = function isValidMsg (g, distance, result) {
109134           var validator = new BufferResultValidator(g, distance, result);
109135           if (!validator.isValid()) { return validator.getErrorMessage() }
109136           return null
109137         };
109138         BufferResultValidator.isValid = function isValid (g, distance, result) {
109139           var validator = new BufferResultValidator(g, distance, result);
109140           if (validator.isValid()) { return true }
109141           return false
109142         };
109143         staticAccessors$40.VERBOSE.get = function () { return false };
109144         staticAccessors$40.MAX_ENV_DIFF_FRAC.get = function () { return 0.012 };
109145
109146         Object.defineProperties( BufferResultValidator, staticAccessors$40 );
109147
109148         // operation.buffer
109149
109150         var BasicSegmentString = function BasicSegmentString () {
109151           this._pts = null;
109152           this._data = null;
109153           var pts = arguments[0];
109154           var data = arguments[1];
109155           this._pts = pts;
109156           this._data = data;
109157         };
109158         BasicSegmentString.prototype.getCoordinates = function getCoordinates () {
109159           return this._pts
109160         };
109161         BasicSegmentString.prototype.size = function size () {
109162           return this._pts.length
109163         };
109164         BasicSegmentString.prototype.getCoordinate = function getCoordinate (i) {
109165           return this._pts[i]
109166         };
109167         BasicSegmentString.prototype.isClosed = function isClosed () {
109168           return this._pts[0].equals(this._pts[this._pts.length - 1])
109169         };
109170         BasicSegmentString.prototype.getSegmentOctant = function getSegmentOctant (index) {
109171           if (index === this._pts.length - 1) { return -1 }
109172           return Octant.octant(this.getCoordinate(index), this.getCoordinate(index + 1))
109173         };
109174         BasicSegmentString.prototype.setData = function setData (data) {
109175           this._data = data;
109176         };
109177         BasicSegmentString.prototype.getData = function getData () {
109178           return this._data
109179         };
109180         BasicSegmentString.prototype.toString = function toString () {
109181           return WKTWriter.toLineString(new CoordinateArraySequence(this._pts))
109182         };
109183         BasicSegmentString.prototype.interfaces_ = function interfaces_ () {
109184           return [SegmentString]
109185         };
109186         BasicSegmentString.prototype.getClass = function getClass () {
109187           return BasicSegmentString
109188         };
109189
109190         var InteriorIntersectionFinder = function InteriorIntersectionFinder () {
109191           this._findAllIntersections = false;
109192           this._isCheckEndSegmentsOnly = false;
109193           this._li = null;
109194           this._interiorIntersection = null;
109195           this._intSegments = null;
109196           this._intersections = new ArrayList();
109197           this._intersectionCount = 0;
109198           this._keepIntersections = true;
109199           var li = arguments[0];
109200           this._li = li;
109201           this._interiorIntersection = null;
109202         };
109203         InteriorIntersectionFinder.prototype.getInteriorIntersection = function getInteriorIntersection () {
109204           return this._interiorIntersection
109205         };
109206         InteriorIntersectionFinder.prototype.setCheckEndSegmentsOnly = function setCheckEndSegmentsOnly (isCheckEndSegmentsOnly) {
109207           this._isCheckEndSegmentsOnly = isCheckEndSegmentsOnly;
109208         };
109209         InteriorIntersectionFinder.prototype.getIntersectionSegments = function getIntersectionSegments () {
109210           return this._intSegments
109211         };
109212         InteriorIntersectionFinder.prototype.count = function count () {
109213           return this._intersectionCount
109214         };
109215         InteriorIntersectionFinder.prototype.getIntersections = function getIntersections () {
109216           return this._intersections
109217         };
109218         InteriorIntersectionFinder.prototype.setFindAllIntersections = function setFindAllIntersections (findAllIntersections) {
109219           this._findAllIntersections = findAllIntersections;
109220         };
109221         InteriorIntersectionFinder.prototype.setKeepIntersections = function setKeepIntersections (keepIntersections) {
109222           this._keepIntersections = keepIntersections;
109223         };
109224         InteriorIntersectionFinder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
109225           if (!this._findAllIntersections && this.hasIntersection()) { return null }
109226           if (e0 === e1 && segIndex0 === segIndex1) { return null }
109227           if (this._isCheckEndSegmentsOnly) {
109228             var isEndSegPresent = this.isEndSegment(e0, segIndex0) || this.isEndSegment(e1, segIndex1);
109229             if (!isEndSegPresent) { return null }
109230           }
109231           var p00 = e0.getCoordinates()[segIndex0];
109232           var p01 = e0.getCoordinates()[segIndex0 + 1];
109233           var p10 = e1.getCoordinates()[segIndex1];
109234           var p11 = e1.getCoordinates()[segIndex1 + 1];
109235           this._li.computeIntersection(p00, p01, p10, p11);
109236           if (this._li.hasIntersection()) {
109237             if (this._li.isInteriorIntersection()) {
109238               this._intSegments = new Array(4).fill(null);
109239               this._intSegments[0] = p00;
109240               this._intSegments[1] = p01;
109241               this._intSegments[2] = p10;
109242               this._intSegments[3] = p11;
109243               this._interiorIntersection = this._li.getIntersection(0);
109244               if (this._keepIntersections) { this._intersections.add(this._interiorIntersection); }
109245               this._intersectionCount++;
109246             }
109247           }
109248         };
109249         InteriorIntersectionFinder.prototype.isEndSegment = function isEndSegment (segStr, index) {
109250           if (index === 0) { return true }
109251           if (index >= segStr.size() - 2) { return true }
109252           return false
109253         };
109254         InteriorIntersectionFinder.prototype.hasIntersection = function hasIntersection () {
109255           return this._interiorIntersection !== null
109256         };
109257         InteriorIntersectionFinder.prototype.isDone = function isDone () {
109258           if (this._findAllIntersections) { return false }
109259           return this._interiorIntersection !== null
109260         };
109261         InteriorIntersectionFinder.prototype.interfaces_ = function interfaces_ () {
109262           return [SegmentIntersector]
109263         };
109264         InteriorIntersectionFinder.prototype.getClass = function getClass () {
109265           return InteriorIntersectionFinder
109266         };
109267         InteriorIntersectionFinder.createAllIntersectionsFinder = function createAllIntersectionsFinder (li) {
109268           var finder = new InteriorIntersectionFinder(li);
109269           finder.setFindAllIntersections(true);
109270           return finder
109271         };
109272         InteriorIntersectionFinder.createAnyIntersectionFinder = function createAnyIntersectionFinder (li) {
109273           return new InteriorIntersectionFinder(li)
109274         };
109275         InteriorIntersectionFinder.createIntersectionCounter = function createIntersectionCounter (li) {
109276           var finder = new InteriorIntersectionFinder(li);
109277           finder.setFindAllIntersections(true);
109278           finder.setKeepIntersections(false);
109279           return finder
109280         };
109281
109282         var FastNodingValidator = function FastNodingValidator () {
109283           this._li = new RobustLineIntersector();
109284           this._segStrings = null;
109285           this._findAllIntersections = false;
109286           this._segInt = null;
109287           this._isValid = true;
109288           var segStrings = arguments[0];
109289           this._segStrings = segStrings;
109290         };
109291         FastNodingValidator.prototype.execute = function execute () {
109292           if (this._segInt !== null) { return null }
109293           this.checkInteriorIntersections();
109294         };
109295         FastNodingValidator.prototype.getIntersections = function getIntersections () {
109296           return this._segInt.getIntersections()
109297         };
109298         FastNodingValidator.prototype.isValid = function isValid () {
109299           this.execute();
109300           return this._isValid
109301         };
109302         FastNodingValidator.prototype.setFindAllIntersections = function setFindAllIntersections (findAllIntersections) {
109303           this._findAllIntersections = findAllIntersections;
109304         };
109305         FastNodingValidator.prototype.checkInteriorIntersections = function checkInteriorIntersections () {
109306           this._isValid = true;
109307           this._segInt = new InteriorIntersectionFinder(this._li);
109308           this._segInt.setFindAllIntersections(this._findAllIntersections);
109309           var noder = new MCIndexNoder();
109310           noder.setSegmentIntersector(this._segInt);
109311           noder.computeNodes(this._segStrings);
109312           if (this._segInt.hasIntersection()) {
109313             this._isValid = false;
109314             return null
109315           }
109316         };
109317         FastNodingValidator.prototype.checkValid = function checkValid () {
109318           this.execute();
109319           if (!this._isValid) { throw new TopologyException(this.getErrorMessage(), this._segInt.getInteriorIntersection()) }
109320         };
109321         FastNodingValidator.prototype.getErrorMessage = function getErrorMessage () {
109322           if (this._isValid) { return 'no intersections found' }
109323           var intSegs = this._segInt.getIntersectionSegments();
109324           return 'found non-noded intersection between ' + WKTWriter.toLineString(intSegs[0], intSegs[1]) + ' and ' + WKTWriter.toLineString(intSegs[2], intSegs[3])
109325         };
109326         FastNodingValidator.prototype.interfaces_ = function interfaces_ () {
109327           return []
109328         };
109329         FastNodingValidator.prototype.getClass = function getClass () {
109330           return FastNodingValidator
109331         };
109332         FastNodingValidator.computeIntersections = function computeIntersections (segStrings) {
109333           var nv = new FastNodingValidator(segStrings);
109334           nv.setFindAllIntersections(true);
109335           nv.isValid();
109336           return nv.getIntersections()
109337         };
109338
109339         var EdgeNodingValidator = function EdgeNodingValidator () {
109340           this._nv = null;
109341           var edges = arguments[0];
109342           this._nv = new FastNodingValidator(EdgeNodingValidator.toSegmentStrings(edges));
109343         };
109344         EdgeNodingValidator.prototype.checkValid = function checkValid () {
109345           this._nv.checkValid();
109346         };
109347         EdgeNodingValidator.prototype.interfaces_ = function interfaces_ () {
109348           return []
109349         };
109350         EdgeNodingValidator.prototype.getClass = function getClass () {
109351           return EdgeNodingValidator
109352         };
109353         EdgeNodingValidator.toSegmentStrings = function toSegmentStrings (edges) {
109354           var segStrings = new ArrayList();
109355           for (var i = edges.iterator(); i.hasNext();) {
109356             var e = i.next();
109357             segStrings.add(new BasicSegmentString(e.getCoordinates(), e));
109358           }
109359           return segStrings
109360         };
109361         EdgeNodingValidator.checkValid = function checkValid (edges) {
109362           var validator = new EdgeNodingValidator(edges);
109363           validator.checkValid();
109364         };
109365
109366         var GeometryCollectionMapper = function GeometryCollectionMapper (mapOp) {
109367           this._mapOp = mapOp;
109368         };
109369         GeometryCollectionMapper.prototype.map = function map (gc) {
109370             var this$1 = this;
109371
109372           var mapped = new ArrayList();
109373           for (var i = 0; i < gc.getNumGeometries(); i++) {
109374             var g = this$1._mapOp.map(gc.getGeometryN(i));
109375             if (!g.isEmpty()) { mapped.add(g); }
109376           }
109377           return gc.getFactory().createGeometryCollection(GeometryFactory.toGeometryArray(mapped))
109378         };
109379         GeometryCollectionMapper.prototype.interfaces_ = function interfaces_ () {
109380           return []
109381         };
109382         GeometryCollectionMapper.prototype.getClass = function getClass () {
109383           return GeometryCollectionMapper
109384         };
109385         GeometryCollectionMapper.map = function map (gc, op) {
109386           var mapper = new GeometryCollectionMapper(op);
109387           return mapper.map(gc)
109388         };
109389
109390         var LineBuilder = function LineBuilder () {
109391           this._op = null;
109392           this._geometryFactory = null;
109393           this._ptLocator = null;
109394           this._lineEdgesList = new ArrayList();
109395           this._resultLineList = new ArrayList();
109396           var op = arguments[0];
109397           var geometryFactory = arguments[1];
109398           var ptLocator = arguments[2];
109399           this._op = op;
109400           this._geometryFactory = geometryFactory;
109401           this._ptLocator = ptLocator;
109402         };
109403         LineBuilder.prototype.collectLines = function collectLines (opCode) {
109404             var this$1 = this;
109405
109406           for (var it = this._op.getGraph().getEdgeEnds().iterator(); it.hasNext();) {
109407             var de = it.next();
109408             this$1.collectLineEdge(de, opCode, this$1._lineEdgesList);
109409             this$1.collectBoundaryTouchEdge(de, opCode, this$1._lineEdgesList);
109410           }
109411         };
109412         LineBuilder.prototype.labelIsolatedLine = function labelIsolatedLine (e, targetIndex) {
109413           var loc = this._ptLocator.locate(e.getCoordinate(), this._op.getArgGeometry(targetIndex));
109414           e.getLabel().setLocation(targetIndex, loc);
109415         };
109416         LineBuilder.prototype.build = function build (opCode) {
109417           this.findCoveredLineEdges();
109418           this.collectLines(opCode);
109419           this.buildLines(opCode);
109420           return this._resultLineList
109421         };
109422         LineBuilder.prototype.collectLineEdge = function collectLineEdge (de, opCode, edges) {
109423           var label = de.getLabel();
109424           var e = de.getEdge();
109425           if (de.isLineEdge()) {
109426             if (!de.isVisited() && OverlayOp.isResultOfOp(label, opCode) && !e.isCovered()) {
109427               edges.add(e);
109428               de.setVisitedEdge(true);
109429             }
109430           }
109431         };
109432         LineBuilder.prototype.findCoveredLineEdges = function findCoveredLineEdges () {
109433             var this$1 = this;
109434
109435           for (var nodeit = this._op.getGraph().getNodes().iterator(); nodeit.hasNext();) {
109436             var node = nodeit.next();
109437             node.getEdges().findCoveredLineEdges();
109438           }
109439           for (var it = this._op.getGraph().getEdgeEnds().iterator(); it.hasNext();) {
109440             var de = it.next();
109441             var e = de.getEdge();
109442             if (de.isLineEdge() && !e.isCoveredSet()) {
109443               var isCovered = this$1._op.isCoveredByA(de.getCoordinate());
109444               e.setCovered(isCovered);
109445             }
109446           }
109447         };
109448         LineBuilder.prototype.labelIsolatedLines = function labelIsolatedLines (edgesList) {
109449             var this$1 = this;
109450
109451           for (var it = edgesList.iterator(); it.hasNext();) {
109452             var e = it.next();
109453             var label = e.getLabel();
109454             if (e.isIsolated()) {
109455               if (label.isNull(0)) { this$1.labelIsolatedLine(e, 0); } else { this$1.labelIsolatedLine(e, 1); }
109456             }
109457           }
109458         };
109459         LineBuilder.prototype.buildLines = function buildLines (opCode) {
109460             var this$1 = this;
109461
109462           for (var it = this._lineEdgesList.iterator(); it.hasNext();) {
109463             var e = it.next();
109464             // const label = e.getLabel()
109465             var line = this$1._geometryFactory.createLineString(e.getCoordinates());
109466             this$1._resultLineList.add(line);
109467             e.setInResult(true);
109468           }
109469         };
109470         LineBuilder.prototype.collectBoundaryTouchEdge = function collectBoundaryTouchEdge (de, opCode, edges) {
109471           var label = de.getLabel();
109472           if (de.isLineEdge()) { return null }
109473           if (de.isVisited()) { return null }
109474           if (de.isInteriorAreaEdge()) { return null }
109475           if (de.getEdge().isInResult()) { return null }
109476           Assert.isTrue(!(de.isInResult() || de.getSym().isInResult()) || !de.getEdge().isInResult());
109477           if (OverlayOp.isResultOfOp(label, opCode) && opCode === OverlayOp.INTERSECTION) {
109478             edges.add(de.getEdge());
109479             de.setVisitedEdge(true);
109480           }
109481         };
109482         LineBuilder.prototype.interfaces_ = function interfaces_ () {
109483           return []
109484         };
109485         LineBuilder.prototype.getClass = function getClass () {
109486           return LineBuilder
109487         };
109488
109489         var PointBuilder = function PointBuilder () {
109490           this._op = null;
109491           this._geometryFactory = null;
109492           this._resultPointList = new ArrayList();
109493           var op = arguments[0];
109494           var geometryFactory = arguments[1];
109495           // const ptLocator = arguments[2]
109496           this._op = op;
109497           this._geometryFactory = geometryFactory;
109498         };
109499         PointBuilder.prototype.filterCoveredNodeToPoint = function filterCoveredNodeToPoint (n) {
109500           var coord = n.getCoordinate();
109501           if (!this._op.isCoveredByLA(coord)) {
109502             var pt = this._geometryFactory.createPoint(coord);
109503             this._resultPointList.add(pt);
109504           }
109505         };
109506         PointBuilder.prototype.extractNonCoveredResultNodes = function extractNonCoveredResultNodes (opCode) {
109507             var this$1 = this;
109508
109509           for (var nodeit = this._op.getGraph().getNodes().iterator(); nodeit.hasNext();) {
109510             var n = nodeit.next();
109511             if (n.isInResult()) { continue }
109512             if (n.isIncidentEdgeInResult()) { continue }
109513             if (n.getEdges().getDegree() === 0 || opCode === OverlayOp.INTERSECTION) {
109514               var label = n.getLabel();
109515               if (OverlayOp.isResultOfOp(label, opCode)) {
109516                 this$1.filterCoveredNodeToPoint(n);
109517               }
109518             }
109519           }
109520         };
109521         PointBuilder.prototype.build = function build (opCode) {
109522           this.extractNonCoveredResultNodes(opCode);
109523           return this._resultPointList
109524         };
109525         PointBuilder.prototype.interfaces_ = function interfaces_ () {
109526           return []
109527         };
109528         PointBuilder.prototype.getClass = function getClass () {
109529           return PointBuilder
109530         };
109531
109532         var GeometryTransformer = function GeometryTransformer () {
109533           this._inputGeom = null;
109534           this._factory = null;
109535           this._pruneEmptyGeometry = true;
109536           this._preserveGeometryCollectionType = true;
109537           this._preserveCollections = false;
109538           this._preserveType = false;
109539         };
109540         GeometryTransformer.prototype.transformPoint = function transformPoint (geom, parent) {
109541           return this._factory.createPoint(this.transformCoordinates(geom.getCoordinateSequence(), geom))
109542         };
109543         GeometryTransformer.prototype.transformPolygon = function transformPolygon (geom, parent) {
109544             var this$1 = this;
109545
109546           var isAllValidLinearRings = true;
109547           var shell = this.transformLinearRing(geom.getExteriorRing(), geom);
109548           if (shell === null || !(shell instanceof LinearRing) || shell.isEmpty()) { isAllValidLinearRings = false; }
109549           var holes = new ArrayList();
109550           for (var i = 0; i < geom.getNumInteriorRing(); i++) {
109551             var hole = this$1.transformLinearRing(geom.getInteriorRingN(i), geom);
109552             if (hole === null || hole.isEmpty()) {
109553               continue
109554             }
109555             if (!(hole instanceof LinearRing)) { isAllValidLinearRings = false; }
109556             holes.add(hole);
109557           }
109558           if (isAllValidLinearRings) { return this._factory.createPolygon(shell, holes.toArray([])); } else {
109559             var components = new ArrayList();
109560             if (shell !== null) { components.add(shell); }
109561             components.addAll(holes);
109562             return this._factory.buildGeometry(components)
109563           }
109564         };
109565         GeometryTransformer.prototype.createCoordinateSequence = function createCoordinateSequence (coords) {
109566           return this._factory.getCoordinateSequenceFactory().create(coords)
109567         };
109568         GeometryTransformer.prototype.getInputGeometry = function getInputGeometry () {
109569           return this._inputGeom
109570         };
109571         GeometryTransformer.prototype.transformMultiLineString = function transformMultiLineString (geom, parent) {
109572             var this$1 = this;
109573
109574           var transGeomList = new ArrayList();
109575           for (var i = 0; i < geom.getNumGeometries(); i++) {
109576             var transformGeom = this$1.transformLineString(geom.getGeometryN(i), geom);
109577             if (transformGeom === null) { continue }
109578             if (transformGeom.isEmpty()) { continue }
109579             transGeomList.add(transformGeom);
109580           }
109581           return this._factory.buildGeometry(transGeomList)
109582         };
109583         GeometryTransformer.prototype.transformCoordinates = function transformCoordinates (coords, parent) {
109584           return this.copy(coords)
109585         };
109586         GeometryTransformer.prototype.transformLineString = function transformLineString (geom, parent) {
109587           return this._factory.createLineString(this.transformCoordinates(geom.getCoordinateSequence(), geom))
109588         };
109589         GeometryTransformer.prototype.transformMultiPoint = function transformMultiPoint (geom, parent) {
109590             var this$1 = this;
109591
109592           var transGeomList = new ArrayList();
109593           for (var i = 0; i < geom.getNumGeometries(); i++) {
109594             var transformGeom = this$1.transformPoint(geom.getGeometryN(i), geom);
109595             if (transformGeom === null) { continue }
109596             if (transformGeom.isEmpty()) { continue }
109597             transGeomList.add(transformGeom);
109598           }
109599           return this._factory.buildGeometry(transGeomList)
109600         };
109601         GeometryTransformer.prototype.transformMultiPolygon = function transformMultiPolygon (geom, parent) {
109602             var this$1 = this;
109603
109604           var transGeomList = new ArrayList();
109605           for (var i = 0; i < geom.getNumGeometries(); i++) {
109606             var transformGeom = this$1.transformPolygon(geom.getGeometryN(i), geom);
109607             if (transformGeom === null) { continue }
109608             if (transformGeom.isEmpty()) { continue }
109609             transGeomList.add(transformGeom);
109610           }
109611           return this._factory.buildGeometry(transGeomList)
109612         };
109613         GeometryTransformer.prototype.copy = function copy (seq) {
109614           return seq.copy()
109615         };
109616         GeometryTransformer.prototype.transformGeometryCollection = function transformGeometryCollection (geom, parent) {
109617             var this$1 = this;
109618
109619           var transGeomList = new ArrayList();
109620           for (var i = 0; i < geom.getNumGeometries(); i++) {
109621             var transformGeom = this$1.transform(geom.getGeometryN(i));
109622             if (transformGeom === null) { continue }
109623             if (this$1._pruneEmptyGeometry && transformGeom.isEmpty()) { continue }
109624             transGeomList.add(transformGeom);
109625           }
109626           if (this._preserveGeometryCollectionType) { return this._factory.createGeometryCollection(GeometryFactory.toGeometryArray(transGeomList)) }
109627           return this._factory.buildGeometry(transGeomList)
109628         };
109629         GeometryTransformer.prototype.transform = function transform (inputGeom) {
109630           this._inputGeom = inputGeom;
109631           this._factory = inputGeom.getFactory();
109632           if (inputGeom instanceof Point$1) { return this.transformPoint(inputGeom, null) }
109633           if (inputGeom instanceof MultiPoint) { return this.transformMultiPoint(inputGeom, null) }
109634           if (inputGeom instanceof LinearRing) { return this.transformLinearRing(inputGeom, null) }
109635           if (inputGeom instanceof LineString) { return this.transformLineString(inputGeom, null) }
109636           if (inputGeom instanceof MultiLineString) { return this.transformMultiLineString(inputGeom, null) }
109637           if (inputGeom instanceof Polygon) { return this.transformPolygon(inputGeom, null) }
109638           if (inputGeom instanceof MultiPolygon) { return this.transformMultiPolygon(inputGeom, null) }
109639           if (inputGeom instanceof GeometryCollection) { return this.transformGeometryCollection(inputGeom, null) }
109640           throw new IllegalArgumentException('Unknown Geometry subtype: ' + inputGeom.getClass().getName())
109641         };
109642         GeometryTransformer.prototype.transformLinearRing = function transformLinearRing (geom, parent) {
109643           var seq = this.transformCoordinates(geom.getCoordinateSequence(), geom);
109644           if (seq === null) { return this._factory.createLinearRing(null) }
109645           var seqSize = seq.size();
109646           if (seqSize > 0 && seqSize < 4 && !this._preserveType) { return this._factory.createLineString(seq) }
109647           return this._factory.createLinearRing(seq)
109648         };
109649         GeometryTransformer.prototype.interfaces_ = function interfaces_ () {
109650           return []
109651         };
109652         GeometryTransformer.prototype.getClass = function getClass () {
109653           return GeometryTransformer
109654         };
109655
109656         var LineStringSnapper = function LineStringSnapper () {
109657           this._snapTolerance = 0.0;
109658           this._srcPts = null;
109659           this._seg = new LineSegment();
109660           this._allowSnappingToSourceVertices = false;
109661           this._isClosed = false;
109662           if (arguments[0] instanceof LineString && typeof arguments[1] === 'number') {
109663             var srcLine = arguments[0];
109664             var snapTolerance = arguments[1];
109665             LineStringSnapper.call(this, srcLine.getCoordinates(), snapTolerance);
109666           } else if (arguments[0] instanceof Array && typeof arguments[1] === 'number') {
109667             var srcPts = arguments[0];
109668             var snapTolerance$1 = arguments[1];
109669             this._srcPts = srcPts;
109670             this._isClosed = LineStringSnapper.isClosed(srcPts);
109671             this._snapTolerance = snapTolerance$1;
109672           }
109673         };
109674         LineStringSnapper.prototype.snapVertices = function snapVertices (srcCoords, snapPts) {
109675             var this$1 = this;
109676
109677           var end = this._isClosed ? srcCoords.size() - 1 : srcCoords.size();
109678           for (var i = 0; i < end; i++) {
109679             var srcPt = srcCoords.get(i);
109680             var snapVert = this$1.findSnapForVertex(srcPt, snapPts);
109681             if (snapVert !== null) {
109682               srcCoords.set(i, new Coordinate(snapVert));
109683               if (i === 0 && this$1._isClosed) { srcCoords.set(srcCoords.size() - 1, new Coordinate(snapVert)); }
109684             }
109685           }
109686         };
109687         LineStringSnapper.prototype.findSnapForVertex = function findSnapForVertex (pt, snapPts) {
109688             var this$1 = this;
109689
109690           for (var i = 0; i < snapPts.length; i++) {
109691             if (pt.equals2D(snapPts[i])) { return null }
109692             if (pt.distance(snapPts[i]) < this$1._snapTolerance) { return snapPts[i] }
109693           }
109694           return null
109695         };
109696         LineStringSnapper.prototype.snapTo = function snapTo (snapPts) {
109697           var coordList = new CoordinateList(this._srcPts);
109698           this.snapVertices(coordList, snapPts);
109699           this.snapSegments(coordList, snapPts);
109700           var newPts = coordList.toCoordinateArray();
109701           return newPts
109702         };
109703         LineStringSnapper.prototype.snapSegments = function snapSegments (srcCoords, snapPts) {
109704             var this$1 = this;
109705
109706           if (snapPts.length === 0) { return null }
109707           var distinctPtCount = snapPts.length;
109708           if (snapPts[0].equals2D(snapPts[snapPts.length - 1])) { distinctPtCount = snapPts.length - 1; }
109709           for (var i = 0; i < distinctPtCount; i++) {
109710             var snapPt = snapPts[i];
109711             var index = this$1.findSegmentIndexToSnap(snapPt, srcCoords);
109712             if (index >= 0) {
109713               srcCoords.add(index + 1, new Coordinate(snapPt), false);
109714             }
109715           }
109716         };
109717         LineStringSnapper.prototype.findSegmentIndexToSnap = function findSegmentIndexToSnap (snapPt, srcCoords) {
109718             var this$1 = this;
109719
109720           var minDist = Double.MAX_VALUE;
109721           var snapIndex = -1;
109722           for (var i = 0; i < srcCoords.size() - 1; i++) {
109723             this$1._seg.p0 = srcCoords.get(i);
109724             this$1._seg.p1 = srcCoords.get(i + 1);
109725             if (this$1._seg.p0.equals2D(snapPt) || this$1._seg.p1.equals2D(snapPt)) {
109726               if (this$1._allowSnappingToSourceVertices) { continue; } else { return -1 }
109727             }
109728             var dist = this$1._seg.distance(snapPt);
109729             if (dist < this$1._snapTolerance && dist < minDist) {
109730               minDist = dist;
109731               snapIndex = i;
109732             }
109733           }
109734           return snapIndex
109735         };
109736         LineStringSnapper.prototype.setAllowSnappingToSourceVertices = function setAllowSnappingToSourceVertices (allowSnappingToSourceVertices) {
109737           this._allowSnappingToSourceVertices = allowSnappingToSourceVertices;
109738         };
109739         LineStringSnapper.prototype.interfaces_ = function interfaces_ () {
109740           return []
109741         };
109742         LineStringSnapper.prototype.getClass = function getClass () {
109743           return LineStringSnapper
109744         };
109745         LineStringSnapper.isClosed = function isClosed (pts) {
109746           if (pts.length <= 1) { return false }
109747           return pts[0].equals2D(pts[pts.length - 1])
109748         };
109749
109750         var GeometrySnapper = function GeometrySnapper (srcGeom) {
109751           this._srcGeom = srcGeom || null;
109752         };
109753
109754         var staticAccessors$41 = { SNAP_PRECISION_FACTOR: { configurable: true } };
109755         GeometrySnapper.prototype.snapTo = function snapTo (snapGeom, snapTolerance) {
109756           var snapPts = this.extractTargetCoordinates(snapGeom);
109757           var snapTrans = new SnapTransformer(snapTolerance, snapPts);
109758           return snapTrans.transform(this._srcGeom)
109759         };
109760         GeometrySnapper.prototype.snapToSelf = function snapToSelf (snapTolerance, cleanResult) {
109761           var snapPts = this.extractTargetCoordinates(this._srcGeom);
109762           var snapTrans = new SnapTransformer(snapTolerance, snapPts, true);
109763           var snappedGeom = snapTrans.transform(this._srcGeom);
109764           var result = snappedGeom;
109765           if (cleanResult && hasInterface(result, Polygonal)) {
109766             result = snappedGeom.buffer(0);
109767           }
109768           return result
109769         };
109770         GeometrySnapper.prototype.computeSnapTolerance = function computeSnapTolerance (ringPts) {
109771           var minSegLen = this.computeMinimumSegmentLength(ringPts);
109772           var snapTol = minSegLen / 10;
109773           return snapTol
109774         };
109775         GeometrySnapper.prototype.extractTargetCoordinates = function extractTargetCoordinates (g) {
109776           var ptSet = new TreeSet();
109777           var pts = g.getCoordinates();
109778           for (var i = 0; i < pts.length; i++) {
109779             ptSet.add(pts[i]);
109780           }
109781           return ptSet.toArray(new Array(0).fill(null))
109782         };
109783         GeometrySnapper.prototype.computeMinimumSegmentLength = function computeMinimumSegmentLength (pts) {
109784           var minSegLen = Double.MAX_VALUE;
109785           for (var i = 0; i < pts.length - 1; i++) {
109786             var segLen = pts[i].distance(pts[i + 1]);
109787             if (segLen < minSegLen) { minSegLen = segLen; }
109788           }
109789           return minSegLen
109790         };
109791         GeometrySnapper.prototype.interfaces_ = function interfaces_ () {
109792           return []
109793         };
109794         GeometrySnapper.prototype.getClass = function getClass () {
109795           return GeometrySnapper
109796         };
109797         GeometrySnapper.snap = function snap (g0, g1, snapTolerance) {
109798           var snapGeom = new Array(2).fill(null);
109799           var snapper0 = new GeometrySnapper(g0);
109800           snapGeom[0] = snapper0.snapTo(g1, snapTolerance);
109801           var snapper1 = new GeometrySnapper(g1);
109802           snapGeom[1] = snapper1.snapTo(snapGeom[0], snapTolerance);
109803           return snapGeom
109804         };
109805         GeometrySnapper.computeOverlaySnapTolerance = function computeOverlaySnapTolerance () {
109806           if (arguments.length === 1) {
109807             var g = arguments[0];
109808             var snapTolerance = GeometrySnapper.computeSizeBasedSnapTolerance(g);
109809             var pm = g.getPrecisionModel();
109810             if (pm.getType() === PrecisionModel.FIXED) {
109811               var fixedSnapTol = 1 / pm.getScale() * 2 / 1.415;
109812               if (fixedSnapTol > snapTolerance) { snapTolerance = fixedSnapTol; }
109813             }
109814             return snapTolerance
109815           } else if (arguments.length === 2) {
109816             var g0 = arguments[0];
109817             var g1 = arguments[1];
109818             return Math.min(GeometrySnapper.computeOverlaySnapTolerance(g0), GeometrySnapper.computeOverlaySnapTolerance(g1))
109819           }
109820         };
109821         GeometrySnapper.computeSizeBasedSnapTolerance = function computeSizeBasedSnapTolerance (g) {
109822           var env = g.getEnvelopeInternal();
109823           var minDimension = Math.min(env.getHeight(), env.getWidth());
109824           var snapTol = minDimension * GeometrySnapper.SNAP_PRECISION_FACTOR;
109825           return snapTol
109826         };
109827         GeometrySnapper.snapToSelf = function snapToSelf (geom, snapTolerance, cleanResult) {
109828           var snapper0 = new GeometrySnapper(geom);
109829           return snapper0.snapToSelf(snapTolerance, cleanResult)
109830         };
109831         staticAccessors$41.SNAP_PRECISION_FACTOR.get = function () { return 1e-9 };
109832
109833         Object.defineProperties( GeometrySnapper, staticAccessors$41 );
109834
109835         var SnapTransformer = (function (GeometryTransformer$$1) {
109836           function SnapTransformer (snapTolerance, snapPts, isSelfSnap) {
109837             GeometryTransformer$$1.call(this);
109838             this._snapTolerance = snapTolerance || null;
109839             this._snapPts = snapPts || null;
109840             this._isSelfSnap = (isSelfSnap !== undefined) ? isSelfSnap : false;
109841           }
109842
109843           if ( GeometryTransformer$$1 ) SnapTransformer.__proto__ = GeometryTransformer$$1;
109844           SnapTransformer.prototype = Object.create( GeometryTransformer$$1 && GeometryTransformer$$1.prototype );
109845           SnapTransformer.prototype.constructor = SnapTransformer;
109846           SnapTransformer.prototype.snapLine = function snapLine (srcPts, snapPts) {
109847             var snapper = new LineStringSnapper(srcPts, this._snapTolerance);
109848             snapper.setAllowSnappingToSourceVertices(this._isSelfSnap);
109849             return snapper.snapTo(snapPts)
109850           };
109851           SnapTransformer.prototype.transformCoordinates = function transformCoordinates (coords, parent) {
109852             var srcPts = coords.toCoordinateArray();
109853             var newPts = this.snapLine(srcPts, this._snapPts);
109854             return this._factory.getCoordinateSequenceFactory().create(newPts)
109855           };
109856           SnapTransformer.prototype.interfaces_ = function interfaces_ () {
109857             return []
109858           };
109859           SnapTransformer.prototype.getClass = function getClass () {
109860             return SnapTransformer
109861           };
109862
109863           return SnapTransformer;
109864         }(GeometryTransformer));
109865
109866         var CommonBits = function CommonBits () {
109867           this._isFirst = true;
109868           this._commonMantissaBitsCount = 53;
109869           this._commonBits = 0;
109870           this._commonSignExp = null;
109871         };
109872         CommonBits.prototype.getCommon = function getCommon () {
109873           return Double.longBitsToDouble(this._commonBits)
109874         };
109875         CommonBits.prototype.add = function add (num) {
109876           var numBits = Double.doubleToLongBits(num);
109877           if (this._isFirst) {
109878             this._commonBits = numBits;
109879             this._commonSignExp = CommonBits.signExpBits(this._commonBits);
109880             this._isFirst = false;
109881             return null
109882           }
109883           var numSignExp = CommonBits.signExpBits(numBits);
109884           if (numSignExp !== this._commonSignExp) {
109885             this._commonBits = 0;
109886             return null
109887           }
109888           this._commonMantissaBitsCount = CommonBits.numCommonMostSigMantissaBits(this._commonBits, numBits);
109889           this._commonBits = CommonBits.zeroLowerBits(this._commonBits, 64 - (12 + this._commonMantissaBitsCount));
109890         };
109891         CommonBits.prototype.toString = function toString () {
109892           if (arguments.length === 1) {
109893             var bits = arguments[0];
109894             var x = Double.longBitsToDouble(bits);
109895             var numStr = Double.toBinaryString(bits);
109896             var padStr = '0000000000000000000000000000000000000000000000000000000000000000' + numStr;
109897             var bitStr = padStr.substring(padStr.length - 64);
109898             var str = bitStr.substring(0, 1) + '  ' + bitStr.substring(1, 12) + '(exp) ' + bitStr.substring(12) + ' [ ' + x + ' ]';
109899             return str
109900           }
109901         };
109902         CommonBits.prototype.interfaces_ = function interfaces_ () {
109903           return []
109904         };
109905         CommonBits.prototype.getClass = function getClass () {
109906           return CommonBits
109907         };
109908         CommonBits.getBit = function getBit (bits, i) {
109909           var mask = 1 << i;
109910           return (bits & mask) !== 0 ? 1 : 0
109911         };
109912         CommonBits.signExpBits = function signExpBits (num) {
109913           return num >> 52
109914         };
109915         CommonBits.zeroLowerBits = function zeroLowerBits (bits, nBits) {
109916           var invMask = (1 << nBits) - 1;
109917           var mask = ~invMask;
109918           var zeroed = bits & mask;
109919           return zeroed
109920         };
109921         CommonBits.numCommonMostSigMantissaBits = function numCommonMostSigMantissaBits (num1, num2) {
109922           var count = 0;
109923           for (var i = 52; i >= 0; i--) {
109924             if (CommonBits.getBit(num1, i) !== CommonBits.getBit(num2, i)) { return count }
109925             count++;
109926           }
109927           return 52
109928         };
109929
109930         var CommonBitsRemover = function CommonBitsRemover () {
109931           this._commonCoord = null;
109932           this._ccFilter = new CommonCoordinateFilter();
109933         };
109934
109935         var staticAccessors$42 = { CommonCoordinateFilter: { configurable: true },Translater: { configurable: true } };
109936         CommonBitsRemover.prototype.addCommonBits = function addCommonBits (geom) {
109937           var trans = new Translater(this._commonCoord);
109938           geom.apply(trans);
109939           geom.geometryChanged();
109940         };
109941         CommonBitsRemover.prototype.removeCommonBits = function removeCommonBits (geom) {
109942           if (this._commonCoord.x === 0.0 && this._commonCoord.y === 0.0) { return geom }
109943           var invCoord = new Coordinate(this._commonCoord);
109944           invCoord.x = -invCoord.x;
109945           invCoord.y = -invCoord.y;
109946           var trans = new Translater(invCoord);
109947           geom.apply(trans);
109948           geom.geometryChanged();
109949           return geom
109950         };
109951         CommonBitsRemover.prototype.getCommonCoordinate = function getCommonCoordinate () {
109952           return this._commonCoord
109953         };
109954         CommonBitsRemover.prototype.add = function add (geom) {
109955           geom.apply(this._ccFilter);
109956           this._commonCoord = this._ccFilter.getCommonCoordinate();
109957         };
109958         CommonBitsRemover.prototype.interfaces_ = function interfaces_ () {
109959           return []
109960         };
109961         CommonBitsRemover.prototype.getClass = function getClass () {
109962           return CommonBitsRemover
109963         };
109964         staticAccessors$42.CommonCoordinateFilter.get = function () { return CommonCoordinateFilter };
109965         staticAccessors$42.Translater.get = function () { return Translater };
109966
109967         Object.defineProperties( CommonBitsRemover, staticAccessors$42 );
109968
109969         var CommonCoordinateFilter = function CommonCoordinateFilter () {
109970           this._commonBitsX = new CommonBits();
109971           this._commonBitsY = new CommonBits();
109972         };
109973         CommonCoordinateFilter.prototype.filter = function filter (coord) {
109974           this._commonBitsX.add(coord.x);
109975           this._commonBitsY.add(coord.y);
109976         };
109977         CommonCoordinateFilter.prototype.getCommonCoordinate = function getCommonCoordinate () {
109978           return new Coordinate(this._commonBitsX.getCommon(), this._commonBitsY.getCommon())
109979         };
109980         CommonCoordinateFilter.prototype.interfaces_ = function interfaces_ () {
109981           return [CoordinateFilter]
109982         };
109983         CommonCoordinateFilter.prototype.getClass = function getClass () {
109984           return CommonCoordinateFilter
109985         };
109986
109987         var Translater = function Translater () {
109988           this.trans = null;
109989           var trans = arguments[0];
109990           this.trans = trans;
109991         };
109992         Translater.prototype.filter = function filter (seq, i) {
109993           var xp = seq.getOrdinate(i, 0) + this.trans.x;
109994           var yp = seq.getOrdinate(i, 1) + this.trans.y;
109995           seq.setOrdinate(i, 0, xp);
109996           seq.setOrdinate(i, 1, yp);
109997         };
109998         Translater.prototype.isDone = function isDone () {
109999           return false
110000         };
110001         Translater.prototype.isGeometryChanged = function isGeometryChanged () {
110002           return true
110003         };
110004         Translater.prototype.interfaces_ = function interfaces_ () {
110005           return [CoordinateSequenceFilter]
110006         };
110007         Translater.prototype.getClass = function getClass () {
110008           return Translater
110009         };
110010
110011         var SnapOverlayOp = function SnapOverlayOp (g1, g2) {
110012           this._geom = new Array(2).fill(null);
110013           this._snapTolerance = null;
110014           this._cbr = null;
110015           this._geom[0] = g1;
110016           this._geom[1] = g2;
110017           this.computeSnapTolerance();
110018         };
110019         SnapOverlayOp.prototype.selfSnap = function selfSnap (geom) {
110020           var snapper0 = new GeometrySnapper(geom);
110021           var snapGeom = snapper0.snapTo(geom, this._snapTolerance);
110022           return snapGeom
110023         };
110024         SnapOverlayOp.prototype.removeCommonBits = function removeCommonBits (geom) {
110025           this._cbr = new CommonBitsRemover();
110026           this._cbr.add(geom[0]);
110027           this._cbr.add(geom[1]);
110028           var remGeom = new Array(2).fill(null);
110029           remGeom[0] = this._cbr.removeCommonBits(geom[0].copy());
110030           remGeom[1] = this._cbr.removeCommonBits(geom[1].copy());
110031           return remGeom
110032         };
110033         SnapOverlayOp.prototype.prepareResult = function prepareResult (geom) {
110034           this._cbr.addCommonBits(geom);
110035           return geom
110036         };
110037         SnapOverlayOp.prototype.getResultGeometry = function getResultGeometry (opCode) {
110038           var prepGeom = this.snap(this._geom);
110039           var result = OverlayOp.overlayOp(prepGeom[0], prepGeom[1], opCode);
110040           return this.prepareResult(result)
110041         };
110042         SnapOverlayOp.prototype.checkValid = function checkValid (g) {
110043           if (!g.isValid()) {
110044             System.out.println('Snapped geometry is invalid');
110045           }
110046         };
110047         SnapOverlayOp.prototype.computeSnapTolerance = function computeSnapTolerance () {
110048           this._snapTolerance = GeometrySnapper.computeOverlaySnapTolerance(this._geom[0], this._geom[1]);
110049         };
110050         SnapOverlayOp.prototype.snap = function snap (geom) {
110051           var remGeom = this.removeCommonBits(geom);
110052           var snapGeom = GeometrySnapper.snap(remGeom[0], remGeom[1], this._snapTolerance);
110053           return snapGeom
110054         };
110055         SnapOverlayOp.prototype.interfaces_ = function interfaces_ () {
110056           return []
110057         };
110058         SnapOverlayOp.prototype.getClass = function getClass () {
110059           return SnapOverlayOp
110060         };
110061         SnapOverlayOp.overlayOp = function overlayOp (g0, g1, opCode) {
110062           var op = new SnapOverlayOp(g0, g1);
110063           return op.getResultGeometry(opCode)
110064         };
110065         SnapOverlayOp.union = function union (g0, g1) {
110066           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.UNION)
110067         };
110068         SnapOverlayOp.intersection = function intersection (g0, g1) {
110069           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.INTERSECTION)
110070         };
110071         SnapOverlayOp.symDifference = function symDifference (g0, g1) {
110072           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.SYMDIFFERENCE)
110073         };
110074         SnapOverlayOp.difference = function difference (g0, g1) {
110075           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.DIFFERENCE)
110076         };
110077
110078         var SnapIfNeededOverlayOp = function SnapIfNeededOverlayOp (g1, g2) {
110079           this._geom = new Array(2).fill(null);
110080           this._geom[0] = g1;
110081           this._geom[1] = g2;
110082         };
110083         SnapIfNeededOverlayOp.prototype.getResultGeometry = function getResultGeometry (opCode) {
110084           var result = null;
110085           var isSuccess = false;
110086           var savedException = null;
110087           try {
110088             result = OverlayOp.overlayOp(this._geom[0], this._geom[1], opCode);
110089             var isValid = true;
110090             if (isValid) { isSuccess = true; }
110091           } catch (ex) {
110092             if (ex instanceof RuntimeException) {
110093               savedException = ex;
110094             } else { throw ex }
110095           } finally {}
110096           if (!isSuccess) {
110097             try {
110098               result = SnapOverlayOp.overlayOp(this._geom[0], this._geom[1], opCode);
110099             } catch (ex) {
110100               if (ex instanceof RuntimeException) {
110101                 throw savedException
110102               } else { throw ex }
110103             } finally {}
110104           }
110105           return result
110106         };
110107         SnapIfNeededOverlayOp.prototype.interfaces_ = function interfaces_ () {
110108           return []
110109         };
110110         SnapIfNeededOverlayOp.prototype.getClass = function getClass () {
110111           return SnapIfNeededOverlayOp
110112         };
110113         SnapIfNeededOverlayOp.overlayOp = function overlayOp (g0, g1, opCode) {
110114           var op = new SnapIfNeededOverlayOp(g0, g1);
110115           return op.getResultGeometry(opCode)
110116         };
110117         SnapIfNeededOverlayOp.union = function union (g0, g1) {
110118           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.UNION)
110119         };
110120         SnapIfNeededOverlayOp.intersection = function intersection (g0, g1) {
110121           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.INTERSECTION)
110122         };
110123         SnapIfNeededOverlayOp.symDifference = function symDifference (g0, g1) {
110124           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.SYMDIFFERENCE)
110125         };
110126         SnapIfNeededOverlayOp.difference = function difference (g0, g1) {
110127           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.DIFFERENCE)
110128         };
110129
110130         var MonotoneChain$2 = function MonotoneChain () {
110131           this.mce = null;
110132           this.chainIndex = null;
110133           var mce = arguments[0];
110134           var chainIndex = arguments[1];
110135           this.mce = mce;
110136           this.chainIndex = chainIndex;
110137         };
110138         MonotoneChain$2.prototype.computeIntersections = function computeIntersections (mc, si) {
110139           this.mce.computeIntersectsForChain(this.chainIndex, mc.mce, mc.chainIndex, si);
110140         };
110141         MonotoneChain$2.prototype.interfaces_ = function interfaces_ () {
110142           return []
110143         };
110144         MonotoneChain$2.prototype.getClass = function getClass () {
110145           return MonotoneChain$2
110146         };
110147
110148         var SweepLineEvent = function SweepLineEvent () {
110149           this._label = null;
110150           this._xValue = null;
110151           this._eventType = null;
110152           this._insertEvent = null;
110153           this._deleteEventIndex = null;
110154           this._obj = null;
110155           if (arguments.length === 2) {
110156             var x = arguments[0];
110157             var insertEvent = arguments[1];
110158             this._eventType = SweepLineEvent.DELETE;
110159             this._xValue = x;
110160             this._insertEvent = insertEvent;
110161           } else if (arguments.length === 3) {
110162             var label = arguments[0];
110163             var x$1 = arguments[1];
110164             var obj = arguments[2];
110165             this._eventType = SweepLineEvent.INSERT;
110166             this._label = label;
110167             this._xValue = x$1;
110168             this._obj = obj;
110169           }
110170         };
110171
110172         var staticAccessors$43 = { INSERT: { configurable: true },DELETE: { configurable: true } };
110173         SweepLineEvent.prototype.isDelete = function isDelete () {
110174           return this._eventType === SweepLineEvent.DELETE
110175         };
110176         SweepLineEvent.prototype.setDeleteEventIndex = function setDeleteEventIndex (deleteEventIndex) {
110177           this._deleteEventIndex = deleteEventIndex;
110178         };
110179         SweepLineEvent.prototype.getObject = function getObject () {
110180           return this._obj
110181         };
110182         SweepLineEvent.prototype.compareTo = function compareTo (o) {
110183           var pe = o;
110184           if (this._xValue < pe._xValue) { return -1 }
110185           if (this._xValue > pe._xValue) { return 1 }
110186           if (this._eventType < pe._eventType) { return -1 }
110187           if (this._eventType > pe._eventType) { return 1 }
110188           return 0
110189         };
110190         SweepLineEvent.prototype.getInsertEvent = function getInsertEvent () {
110191           return this._insertEvent
110192         };
110193         SweepLineEvent.prototype.isInsert = function isInsert () {
110194           return this._eventType === SweepLineEvent.INSERT
110195         };
110196         SweepLineEvent.prototype.isSameLabel = function isSameLabel (ev) {
110197           if (this._label === null) { return false }
110198           return this._label === ev._label
110199         };
110200         SweepLineEvent.prototype.getDeleteEventIndex = function getDeleteEventIndex () {
110201           return this._deleteEventIndex
110202         };
110203         SweepLineEvent.prototype.interfaces_ = function interfaces_ () {
110204           return [Comparable]
110205         };
110206         SweepLineEvent.prototype.getClass = function getClass () {
110207           return SweepLineEvent
110208         };
110209         staticAccessors$43.INSERT.get = function () { return 1 };
110210         staticAccessors$43.DELETE.get = function () { return 2 };
110211
110212         Object.defineProperties( SweepLineEvent, staticAccessors$43 );
110213
110214         var EdgeSetIntersector = function EdgeSetIntersector () {};
110215
110216         EdgeSetIntersector.prototype.interfaces_ = function interfaces_ () {
110217           return []
110218         };
110219         EdgeSetIntersector.prototype.getClass = function getClass () {
110220           return EdgeSetIntersector
110221         };
110222
110223         var SegmentIntersector$2 = function SegmentIntersector () {
110224           this._hasIntersection = false;
110225           this._hasProper = false;
110226           this._hasProperInterior = false;
110227           this._properIntersectionPoint = null;
110228           this._li = null;
110229           this._includeProper = null;
110230           this._recordIsolated = null;
110231           this._isSelfIntersection = null;
110232           this._numIntersections = 0;
110233           this.numTests = 0;
110234           this._bdyNodes = null;
110235           this._isDone = false;
110236           this._isDoneWhenProperInt = false;
110237           var li = arguments[0];
110238           var includeProper = arguments[1];
110239           var recordIsolated = arguments[2];
110240           this._li = li;
110241           this._includeProper = includeProper;
110242           this._recordIsolated = recordIsolated;
110243         };
110244         SegmentIntersector$2.prototype.isTrivialIntersection = function isTrivialIntersection (e0, segIndex0, e1, segIndex1) {
110245           if (e0 === e1) {
110246             if (this._li.getIntersectionNum() === 1) {
110247               if (SegmentIntersector$2.isAdjacentSegments(segIndex0, segIndex1)) { return true }
110248               if (e0.isClosed()) {
110249                 var maxSegIndex = e0.getNumPoints() - 1;
110250                 if ((segIndex0 === 0 && segIndex1 === maxSegIndex) ||
110251                     (segIndex1 === 0 && segIndex0 === maxSegIndex)) {
110252                   return true
110253                 }
110254               }
110255             }
110256           }
110257           return false
110258         };
110259         SegmentIntersector$2.prototype.getProperIntersectionPoint = function getProperIntersectionPoint () {
110260           return this._properIntersectionPoint
110261         };
110262         SegmentIntersector$2.prototype.setIsDoneIfProperInt = function setIsDoneIfProperInt (isDoneWhenProperInt) {
110263           this._isDoneWhenProperInt = isDoneWhenProperInt;
110264         };
110265         SegmentIntersector$2.prototype.hasProperInteriorIntersection = function hasProperInteriorIntersection () {
110266           return this._hasProperInterior
110267         };
110268         SegmentIntersector$2.prototype.isBoundaryPointInternal = function isBoundaryPointInternal (li, bdyNodes) {
110269           for (var i = bdyNodes.iterator(); i.hasNext();) {
110270             var node = i.next();
110271             var pt = node.getCoordinate();
110272             if (li.isIntersection(pt)) { return true }
110273           }
110274           return false
110275         };
110276         SegmentIntersector$2.prototype.hasProperIntersection = function hasProperIntersection () {
110277           return this._hasProper
110278         };
110279         SegmentIntersector$2.prototype.hasIntersection = function hasIntersection () {
110280           return this._hasIntersection
110281         };
110282         SegmentIntersector$2.prototype.isDone = function isDone () {
110283           return this._isDone
110284         };
110285         SegmentIntersector$2.prototype.isBoundaryPoint = function isBoundaryPoint (li, bdyNodes) {
110286           if (bdyNodes === null) { return false }
110287           if (this.isBoundaryPointInternal(li, bdyNodes[0])) { return true }
110288           if (this.isBoundaryPointInternal(li, bdyNodes[1])) { return true }
110289           return false
110290         };
110291         SegmentIntersector$2.prototype.setBoundaryNodes = function setBoundaryNodes (bdyNodes0, bdyNodes1) {
110292           this._bdyNodes = new Array(2).fill(null);
110293           this._bdyNodes[0] = bdyNodes0;
110294           this._bdyNodes[1] = bdyNodes1;
110295         };
110296         SegmentIntersector$2.prototype.addIntersections = function addIntersections (e0, segIndex0, e1, segIndex1) {
110297           if (e0 === e1 && segIndex0 === segIndex1) { return null }
110298           this.numTests++;
110299           var p00 = e0.getCoordinates()[segIndex0];
110300           var p01 = e0.getCoordinates()[segIndex0 + 1];
110301           var p10 = e1.getCoordinates()[segIndex1];
110302           var p11 = e1.getCoordinates()[segIndex1 + 1];
110303           this._li.computeIntersection(p00, p01, p10, p11);
110304           if (this._li.hasIntersection()) {
110305             if (this._recordIsolated) {
110306               e0.setIsolated(false);
110307               e1.setIsolated(false);
110308             }
110309             this._numIntersections++;
110310             if (!this.isTrivialIntersection(e0, segIndex0, e1, segIndex1)) {
110311               this._hasIntersection = true;
110312               if (this._includeProper || !this._li.isProper()) {
110313                 e0.addIntersections(this._li, segIndex0, 0);
110314                 e1.addIntersections(this._li, segIndex1, 1);
110315               }
110316               if (this._li.isProper()) {
110317                 this._properIntersectionPoint = this._li.getIntersection(0).copy();
110318                 this._hasProper = true;
110319                 if (this._isDoneWhenProperInt) {
110320                   this._isDone = true;
110321                 }
110322                 if (!this.isBoundaryPoint(this._li, this._bdyNodes)) { this._hasProperInterior = true; }
110323               }
110324             }
110325           }
110326         };
110327         SegmentIntersector$2.prototype.interfaces_ = function interfaces_ () {
110328           return []
110329         };
110330         SegmentIntersector$2.prototype.getClass = function getClass () {
110331           return SegmentIntersector$2
110332         };
110333         SegmentIntersector$2.isAdjacentSegments = function isAdjacentSegments (i1, i2) {
110334           return Math.abs(i1 - i2) === 1
110335         };
110336
110337         var SimpleMCSweepLineIntersector = (function (EdgeSetIntersector$$1) {
110338           function SimpleMCSweepLineIntersector () {
110339             EdgeSetIntersector$$1.call(this);
110340             this.events = new ArrayList();
110341             this.nOverlaps = null;
110342           }
110343
110344           if ( EdgeSetIntersector$$1 ) SimpleMCSweepLineIntersector.__proto__ = EdgeSetIntersector$$1;
110345           SimpleMCSweepLineIntersector.prototype = Object.create( EdgeSetIntersector$$1 && EdgeSetIntersector$$1.prototype );
110346           SimpleMCSweepLineIntersector.prototype.constructor = SimpleMCSweepLineIntersector;
110347           SimpleMCSweepLineIntersector.prototype.prepareEvents = function prepareEvents () {
110348             var this$1 = this;
110349
110350             Collections.sort(this.events);
110351             for (var i = 0; i < this.events.size(); i++) {
110352               var ev = this$1.events.get(i);
110353               if (ev.isDelete()) {
110354                 ev.getInsertEvent().setDeleteEventIndex(i);
110355               }
110356             }
110357           };
110358           SimpleMCSweepLineIntersector.prototype.computeIntersections = function computeIntersections () {
110359             var this$1 = this;
110360
110361             if (arguments.length === 1) {
110362               var si = arguments[0];
110363               this.nOverlaps = 0;
110364               this.prepareEvents();
110365               for (var i = 0; i < this.events.size(); i++) {
110366                 var ev = this$1.events.get(i);
110367                 if (ev.isInsert()) {
110368                   this$1.processOverlaps(i, ev.getDeleteEventIndex(), ev, si);
110369                 }
110370                 if (si.isDone()) {
110371                   break
110372                 }
110373               }
110374             } else if (arguments.length === 3) {
110375               if (arguments[2] instanceof SegmentIntersector$2 && (hasInterface(arguments[0], List) && hasInterface(arguments[1], List))) {
110376                 var edges0 = arguments[0];
110377                 var edges1 = arguments[1];
110378                 var si$1 = arguments[2];
110379                 this.addEdges(edges0, edges0);
110380                 this.addEdges(edges1, edges1);
110381                 this.computeIntersections(si$1);
110382               } else if (typeof arguments[2] === 'boolean' && (hasInterface(arguments[0], List) && arguments[1] instanceof SegmentIntersector$2)) {
110383                 var edges = arguments[0];
110384                 var si$2 = arguments[1];
110385                 var testAllSegments = arguments[2];
110386                 if (testAllSegments) { this.addEdges(edges, null); } else { this.addEdges(edges); }
110387                 this.computeIntersections(si$2);
110388               }
110389             }
110390           };
110391           SimpleMCSweepLineIntersector.prototype.addEdge = function addEdge (edge, edgeSet) {
110392             var this$1 = this;
110393
110394             var mce = edge.getMonotoneChainEdge();
110395             var startIndex = mce.getStartIndexes();
110396             for (var i = 0; i < startIndex.length - 1; i++) {
110397               var mc = new MonotoneChain$2(mce, i);
110398               var insertEvent = new SweepLineEvent(edgeSet, mce.getMinX(i), mc);
110399               this$1.events.add(insertEvent);
110400               this$1.events.add(new SweepLineEvent(mce.getMaxX(i), insertEvent));
110401             }
110402           };
110403           SimpleMCSweepLineIntersector.prototype.processOverlaps = function processOverlaps (start, end, ev0, si) {
110404             var this$1 = this;
110405
110406             var mc0 = ev0.getObject();
110407             for (var i = start; i < end; i++) {
110408               var ev1 = this$1.events.get(i);
110409               if (ev1.isInsert()) {
110410                 var mc1 = ev1.getObject();
110411                 if (!ev0.isSameLabel(ev1)) {
110412                   mc0.computeIntersections(mc1, si);
110413                   this$1.nOverlaps++;
110414                 }
110415               }
110416             }
110417           };
110418           SimpleMCSweepLineIntersector.prototype.addEdges = function addEdges () {
110419             var this$1 = this;
110420
110421             if (arguments.length === 1) {
110422               var edges = arguments[0];
110423               for (var i = edges.iterator(); i.hasNext();) {
110424                 var edge = i.next();
110425                 this$1.addEdge(edge, edge);
110426               }
110427             } else if (arguments.length === 2) {
110428               var edges$1 = arguments[0];
110429               var edgeSet = arguments[1];
110430               for (var i$1 = edges$1.iterator(); i$1.hasNext();) {
110431                 var edge$1 = i$1.next();
110432                 this$1.addEdge(edge$1, edgeSet);
110433               }
110434             }
110435           };
110436           SimpleMCSweepLineIntersector.prototype.interfaces_ = function interfaces_ () {
110437             return []
110438           };
110439           SimpleMCSweepLineIntersector.prototype.getClass = function getClass () {
110440             return SimpleMCSweepLineIntersector
110441           };
110442
110443           return SimpleMCSweepLineIntersector;
110444         }(EdgeSetIntersector));
110445
110446         var IntervalRTreeNode = function IntervalRTreeNode () {
110447           this._min = Double.POSITIVE_INFINITY;
110448           this._max = Double.NEGATIVE_INFINITY;
110449         };
110450
110451         var staticAccessors$45 = { NodeComparator: { configurable: true } };
110452         IntervalRTreeNode.prototype.getMin = function getMin () {
110453           return this._min
110454         };
110455         IntervalRTreeNode.prototype.intersects = function intersects (queryMin, queryMax) {
110456           if (this._min > queryMax || this._max < queryMin) { return false }
110457           return true
110458         };
110459         IntervalRTreeNode.prototype.getMax = function getMax () {
110460           return this._max
110461         };
110462         IntervalRTreeNode.prototype.toString = function toString () {
110463           return WKTWriter.toLineString(new Coordinate(this._min, 0), new Coordinate(this._max, 0))
110464         };
110465         IntervalRTreeNode.prototype.interfaces_ = function interfaces_ () {
110466           return []
110467         };
110468         IntervalRTreeNode.prototype.getClass = function getClass () {
110469           return IntervalRTreeNode
110470         };
110471         staticAccessors$45.NodeComparator.get = function () { return NodeComparator };
110472
110473         Object.defineProperties( IntervalRTreeNode, staticAccessors$45 );
110474
110475         var NodeComparator = function NodeComparator () {};
110476
110477         NodeComparator.prototype.compare = function compare (o1, o2) {
110478           var n1 = o1;
110479           var n2 = o2;
110480           var mid1 = (n1._min + n1._max) / 2;
110481           var mid2 = (n2._min + n2._max) / 2;
110482           if (mid1 < mid2) { return -1 }
110483           if (mid1 > mid2) { return 1 }
110484           return 0
110485         };
110486         NodeComparator.prototype.interfaces_ = function interfaces_ () {
110487           return [Comparator]
110488         };
110489         NodeComparator.prototype.getClass = function getClass () {
110490           return NodeComparator
110491         };
110492
110493         var IntervalRTreeLeafNode = (function (IntervalRTreeNode$$1) {
110494           function IntervalRTreeLeafNode () {
110495             IntervalRTreeNode$$1.call(this);
110496             this._item = null;
110497             var min = arguments[0];
110498             var max = arguments[1];
110499             var item = arguments[2];
110500             this._min = min;
110501             this._max = max;
110502             this._item = item;
110503           }
110504
110505           if ( IntervalRTreeNode$$1 ) IntervalRTreeLeafNode.__proto__ = IntervalRTreeNode$$1;
110506           IntervalRTreeLeafNode.prototype = Object.create( IntervalRTreeNode$$1 && IntervalRTreeNode$$1.prototype );
110507           IntervalRTreeLeafNode.prototype.constructor = IntervalRTreeLeafNode;
110508           IntervalRTreeLeafNode.prototype.query = function query (queryMin, queryMax, visitor) {
110509             if (!this.intersects(queryMin, queryMax)) { return null }
110510             visitor.visitItem(this._item);
110511           };
110512           IntervalRTreeLeafNode.prototype.interfaces_ = function interfaces_ () {
110513             return []
110514           };
110515           IntervalRTreeLeafNode.prototype.getClass = function getClass () {
110516             return IntervalRTreeLeafNode
110517           };
110518
110519           return IntervalRTreeLeafNode;
110520         }(IntervalRTreeNode));
110521
110522         var IntervalRTreeBranchNode = (function (IntervalRTreeNode$$1) {
110523           function IntervalRTreeBranchNode () {
110524             IntervalRTreeNode$$1.call(this);
110525             this._node1 = null;
110526             this._node2 = null;
110527             var n1 = arguments[0];
110528             var n2 = arguments[1];
110529             this._node1 = n1;
110530             this._node2 = n2;
110531             this.buildExtent(this._node1, this._node2);
110532           }
110533
110534           if ( IntervalRTreeNode$$1 ) IntervalRTreeBranchNode.__proto__ = IntervalRTreeNode$$1;
110535           IntervalRTreeBranchNode.prototype = Object.create( IntervalRTreeNode$$1 && IntervalRTreeNode$$1.prototype );
110536           IntervalRTreeBranchNode.prototype.constructor = IntervalRTreeBranchNode;
110537           IntervalRTreeBranchNode.prototype.buildExtent = function buildExtent (n1, n2) {
110538             this._min = Math.min(n1._min, n2._min);
110539             this._max = Math.max(n1._max, n2._max);
110540           };
110541           IntervalRTreeBranchNode.prototype.query = function query (queryMin, queryMax, visitor) {
110542             if (!this.intersects(queryMin, queryMax)) {
110543               return null
110544             }
110545             if (this._node1 !== null) { this._node1.query(queryMin, queryMax, visitor); }
110546             if (this._node2 !== null) { this._node2.query(queryMin, queryMax, visitor); }
110547           };
110548           IntervalRTreeBranchNode.prototype.interfaces_ = function interfaces_ () {
110549             return []
110550           };
110551           IntervalRTreeBranchNode.prototype.getClass = function getClass () {
110552             return IntervalRTreeBranchNode
110553           };
110554
110555           return IntervalRTreeBranchNode;
110556         }(IntervalRTreeNode));
110557
110558         var SortedPackedIntervalRTree = function SortedPackedIntervalRTree () {
110559           this._leaves = new ArrayList();
110560           this._root = null;
110561           this._level = 0;
110562         };
110563         SortedPackedIntervalRTree.prototype.buildTree = function buildTree () {
110564             var this$1 = this;
110565
110566           Collections.sort(this._leaves, new IntervalRTreeNode.NodeComparator());
110567           var src = this._leaves;
110568           var temp = null;
110569           var dest = new ArrayList();
110570           while (true) {
110571             this$1.buildLevel(src, dest);
110572             if (dest.size() === 1) { return dest.get(0) }
110573             temp = src;
110574             src = dest;
110575             dest = temp;
110576           }
110577         };
110578         SortedPackedIntervalRTree.prototype.insert = function insert (min, max, item) {
110579           if (this._root !== null) { throw new Error('Index cannot be added to once it has been queried') }
110580           this._leaves.add(new IntervalRTreeLeafNode(min, max, item));
110581         };
110582         SortedPackedIntervalRTree.prototype.query = function query (min, max, visitor) {
110583           this.init();
110584           this._root.query(min, max, visitor);
110585         };
110586         SortedPackedIntervalRTree.prototype.buildRoot = function buildRoot () {
110587           if (this._root !== null) { return null }
110588           this._root = this.buildTree();
110589         };
110590         SortedPackedIntervalRTree.prototype.printNode = function printNode (node) {
110591           System.out.println(WKTWriter.toLineString(new Coordinate(node._min, this._level), new Coordinate(node._max, this._level)));
110592         };
110593         SortedPackedIntervalRTree.prototype.init = function init () {
110594           if (this._root !== null) { return null }
110595           this.buildRoot();
110596         };
110597         SortedPackedIntervalRTree.prototype.buildLevel = function buildLevel (src, dest) {
110598           this._level++;
110599           dest.clear();
110600           for (var i = 0; i < src.size(); i += 2) {
110601             var n1 = src.get(i);
110602             var n2 = i + 1 < src.size() ? src.get(i) : null;
110603             if (n2 === null) {
110604               dest.add(n1);
110605             } else {
110606               var node = new IntervalRTreeBranchNode(src.get(i), src.get(i + 1));
110607               dest.add(node);
110608             }
110609           }
110610         };
110611         SortedPackedIntervalRTree.prototype.interfaces_ = function interfaces_ () {
110612           return []
110613         };
110614         SortedPackedIntervalRTree.prototype.getClass = function getClass () {
110615           return SortedPackedIntervalRTree
110616         };
110617
110618         var ArrayListVisitor = function ArrayListVisitor () {
110619           this._items = new ArrayList();
110620         };
110621         ArrayListVisitor.prototype.visitItem = function visitItem (item) {
110622           this._items.add(item);
110623         };
110624         ArrayListVisitor.prototype.getItems = function getItems () {
110625           return this._items
110626         };
110627         ArrayListVisitor.prototype.interfaces_ = function interfaces_ () {
110628           return [ItemVisitor]
110629         };
110630         ArrayListVisitor.prototype.getClass = function getClass () {
110631           return ArrayListVisitor
110632         };
110633
110634         var IndexedPointInAreaLocator = function IndexedPointInAreaLocator () {
110635           this._index = null;
110636           var g = arguments[0];
110637           if (!hasInterface(g, Polygonal)) { throw new IllegalArgumentException('Argument must be Polygonal') }
110638           this._index = new IntervalIndexedGeometry(g);
110639         };
110640
110641         var staticAccessors$44 = { SegmentVisitor: { configurable: true },IntervalIndexedGeometry: { configurable: true } };
110642         IndexedPointInAreaLocator.prototype.locate = function locate (p) {
110643           var rcc = new RayCrossingCounter(p);
110644           var visitor = new SegmentVisitor(rcc);
110645           this._index.query(p.y, p.y, visitor);
110646           return rcc.getLocation()
110647         };
110648         IndexedPointInAreaLocator.prototype.interfaces_ = function interfaces_ () {
110649           return [PointOnGeometryLocator]
110650         };
110651         IndexedPointInAreaLocator.prototype.getClass = function getClass () {
110652           return IndexedPointInAreaLocator
110653         };
110654         staticAccessors$44.SegmentVisitor.get = function () { return SegmentVisitor };
110655         staticAccessors$44.IntervalIndexedGeometry.get = function () { return IntervalIndexedGeometry };
110656
110657         Object.defineProperties( IndexedPointInAreaLocator, staticAccessors$44 );
110658
110659         var SegmentVisitor = function SegmentVisitor () {
110660           this._counter = null;
110661           var counter = arguments[0];
110662           this._counter = counter;
110663         };
110664         SegmentVisitor.prototype.visitItem = function visitItem (item) {
110665           var seg = item;
110666           this._counter.countSegment(seg.getCoordinate(0), seg.getCoordinate(1));
110667         };
110668         SegmentVisitor.prototype.interfaces_ = function interfaces_ () {
110669           return [ItemVisitor]
110670         };
110671         SegmentVisitor.prototype.getClass = function getClass () {
110672           return SegmentVisitor
110673         };
110674
110675         var IntervalIndexedGeometry = function IntervalIndexedGeometry () {
110676           this._index = new SortedPackedIntervalRTree();
110677           var geom = arguments[0];
110678           this.init(geom);
110679         };
110680         IntervalIndexedGeometry.prototype.init = function init (geom) {
110681             var this$1 = this;
110682
110683           var lines = LinearComponentExtracter.getLines(geom);
110684           for (var i = lines.iterator(); i.hasNext();) {
110685             var line = i.next();
110686             var pts = line.getCoordinates();
110687             this$1.addLine(pts);
110688           }
110689         };
110690         IntervalIndexedGeometry.prototype.addLine = function addLine (pts) {
110691             var this$1 = this;
110692
110693           for (var i = 1; i < pts.length; i++) {
110694             var seg = new LineSegment(pts[i - 1], pts[i]);
110695             var min = Math.min(seg.p0.y, seg.p1.y);
110696             var max = Math.max(seg.p0.y, seg.p1.y);
110697             this$1._index.insert(min, max, seg);
110698           }
110699         };
110700         IntervalIndexedGeometry.prototype.query = function query () {
110701           if (arguments.length === 2) {
110702             var min = arguments[0];
110703             var max = arguments[1];
110704             var visitor = new ArrayListVisitor();
110705             this._index.query(min, max, visitor);
110706             return visitor.getItems()
110707           } else if (arguments.length === 3) {
110708             var min$1 = arguments[0];
110709             var max$1 = arguments[1];
110710             var visitor$1 = arguments[2];
110711             this._index.query(min$1, max$1, visitor$1);
110712           }
110713         };
110714         IntervalIndexedGeometry.prototype.interfaces_ = function interfaces_ () {
110715           return []
110716         };
110717         IntervalIndexedGeometry.prototype.getClass = function getClass () {
110718           return IntervalIndexedGeometry
110719         };
110720
110721         var GeometryGraph = (function (PlanarGraph$$1) {
110722           function GeometryGraph () {
110723             PlanarGraph$$1.call(this);
110724             this._parentGeom = null;
110725             this._lineEdgeMap = new HashMap();
110726             this._boundaryNodeRule = null;
110727             this._useBoundaryDeterminationRule = true;
110728             this._argIndex = null;
110729             this._boundaryNodes = null;
110730             this._hasTooFewPoints = false;
110731             this._invalidPoint = null;
110732             this._areaPtLocator = null;
110733             this._ptLocator = new PointLocator();
110734             if (arguments.length === 2) {
110735               var argIndex = arguments[0];
110736               var parentGeom = arguments[1];
110737               var boundaryNodeRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
110738               this._argIndex = argIndex;
110739               this._parentGeom = parentGeom;
110740               this._boundaryNodeRule = boundaryNodeRule;
110741               if (parentGeom !== null) {
110742                 this.add(parentGeom);
110743               }
110744             } else if (arguments.length === 3) {
110745               var argIndex$1 = arguments[0];
110746               var parentGeom$1 = arguments[1];
110747               var boundaryNodeRule$1 = arguments[2];
110748               this._argIndex = argIndex$1;
110749               this._parentGeom = parentGeom$1;
110750               this._boundaryNodeRule = boundaryNodeRule$1;
110751               if (parentGeom$1 !== null) {
110752                 this.add(parentGeom$1);
110753               }
110754             }
110755           }
110756
110757           if ( PlanarGraph$$1 ) GeometryGraph.__proto__ = PlanarGraph$$1;
110758           GeometryGraph.prototype = Object.create( PlanarGraph$$1 && PlanarGraph$$1.prototype );
110759           GeometryGraph.prototype.constructor = GeometryGraph;
110760           GeometryGraph.prototype.insertBoundaryPoint = function insertBoundaryPoint (argIndex, coord) {
110761             var n = this._nodes.addNode(coord);
110762             var lbl = n.getLabel();
110763             var boundaryCount = 1;
110764             var loc = Location.NONE;
110765             loc = lbl.getLocation(argIndex, Position.ON);
110766             if (loc === Location.BOUNDARY) { boundaryCount++; }
110767             var newLoc = GeometryGraph.determineBoundary(this._boundaryNodeRule, boundaryCount);
110768             lbl.setLocation(argIndex, newLoc);
110769           };
110770           GeometryGraph.prototype.computeSelfNodes = function computeSelfNodes () {
110771             if (arguments.length === 2) {
110772               var li = arguments[0];
110773               var computeRingSelfNodes = arguments[1];
110774               return this.computeSelfNodes(li, computeRingSelfNodes, false)
110775             } else if (arguments.length === 3) {
110776               var li$1 = arguments[0];
110777               var computeRingSelfNodes$1 = arguments[1];
110778               var isDoneIfProperInt = arguments[2];
110779               var si = new SegmentIntersector$2(li$1, true, false);
110780               si.setIsDoneIfProperInt(isDoneIfProperInt);
110781               var esi = this.createEdgeSetIntersector();
110782               var isRings = this._parentGeom instanceof LinearRing || this._parentGeom instanceof Polygon || this._parentGeom instanceof MultiPolygon;
110783               var computeAllSegments = computeRingSelfNodes$1 || !isRings;
110784               esi.computeIntersections(this._edges, si, computeAllSegments);
110785               this.addSelfIntersectionNodes(this._argIndex);
110786               return si
110787             }
110788           };
110789           GeometryGraph.prototype.computeSplitEdges = function computeSplitEdges (edgelist) {
110790             for (var i = this._edges.iterator(); i.hasNext();) {
110791               var e = i.next();
110792               e.eiList.addSplitEdges(edgelist);
110793             }
110794           };
110795           GeometryGraph.prototype.computeEdgeIntersections = function computeEdgeIntersections (g, li, includeProper) {
110796             var si = new SegmentIntersector$2(li, includeProper, true);
110797             si.setBoundaryNodes(this.getBoundaryNodes(), g.getBoundaryNodes());
110798             var esi = this.createEdgeSetIntersector();
110799             esi.computeIntersections(this._edges, g._edges, si);
110800             return si
110801           };
110802           GeometryGraph.prototype.getGeometry = function getGeometry () {
110803             return this._parentGeom
110804           };
110805           GeometryGraph.prototype.getBoundaryNodeRule = function getBoundaryNodeRule () {
110806             return this._boundaryNodeRule
110807           };
110808           GeometryGraph.prototype.hasTooFewPoints = function hasTooFewPoints () {
110809             return this._hasTooFewPoints
110810           };
110811           GeometryGraph.prototype.addPoint = function addPoint () {
110812             if (arguments[0] instanceof Point$1) {
110813               var p = arguments[0];
110814               var coord = p.getCoordinate();
110815               this.insertPoint(this._argIndex, coord, Location.INTERIOR);
110816             } else if (arguments[0] instanceof Coordinate) {
110817               var pt = arguments[0];
110818               this.insertPoint(this._argIndex, pt, Location.INTERIOR);
110819             }
110820           };
110821           GeometryGraph.prototype.addPolygon = function addPolygon (p) {
110822             var this$1 = this;
110823
110824             this.addPolygonRing(p.getExteriorRing(), Location.EXTERIOR, Location.INTERIOR);
110825             for (var i = 0; i < p.getNumInteriorRing(); i++) {
110826               var hole = p.getInteriorRingN(i);
110827               this$1.addPolygonRing(hole, Location.INTERIOR, Location.EXTERIOR);
110828             }
110829           };
110830           GeometryGraph.prototype.addEdge = function addEdge (e) {
110831             this.insertEdge(e);
110832             var coord = e.getCoordinates();
110833             this.insertPoint(this._argIndex, coord[0], Location.BOUNDARY);
110834             this.insertPoint(this._argIndex, coord[coord.length - 1], Location.BOUNDARY);
110835           };
110836           GeometryGraph.prototype.addLineString = function addLineString (line) {
110837             var coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
110838             if (coord.length < 2) {
110839               this._hasTooFewPoints = true;
110840               this._invalidPoint = coord[0];
110841               return null
110842             }
110843             var e = new Edge(coord, new Label(this._argIndex, Location.INTERIOR));
110844             this._lineEdgeMap.put(line, e);
110845             this.insertEdge(e);
110846             Assert.isTrue(coord.length >= 2, 'found LineString with single point');
110847             this.insertBoundaryPoint(this._argIndex, coord[0]);
110848             this.insertBoundaryPoint(this._argIndex, coord[coord.length - 1]);
110849           };
110850           GeometryGraph.prototype.getInvalidPoint = function getInvalidPoint () {
110851             return this._invalidPoint
110852           };
110853           GeometryGraph.prototype.getBoundaryPoints = function getBoundaryPoints () {
110854             var coll = this.getBoundaryNodes();
110855             var pts = new Array(coll.size()).fill(null);
110856             var i = 0;
110857             for (var it = coll.iterator(); it.hasNext();) {
110858               var node = it.next();
110859               pts[i++] = node.getCoordinate().copy();
110860             }
110861             return pts
110862           };
110863           GeometryGraph.prototype.getBoundaryNodes = function getBoundaryNodes () {
110864             if (this._boundaryNodes === null) { this._boundaryNodes = this._nodes.getBoundaryNodes(this._argIndex); }
110865             return this._boundaryNodes
110866           };
110867           GeometryGraph.prototype.addSelfIntersectionNode = function addSelfIntersectionNode (argIndex, coord, loc) {
110868             if (this.isBoundaryNode(argIndex, coord)) { return null }
110869             if (loc === Location.BOUNDARY && this._useBoundaryDeterminationRule) { this.insertBoundaryPoint(argIndex, coord); } else { this.insertPoint(argIndex, coord, loc); }
110870           };
110871           GeometryGraph.prototype.addPolygonRing = function addPolygonRing (lr, cwLeft, cwRight) {
110872             if (lr.isEmpty()) { return null }
110873             var coord = CoordinateArrays.removeRepeatedPoints(lr.getCoordinates());
110874             if (coord.length < 4) {
110875               this._hasTooFewPoints = true;
110876               this._invalidPoint = coord[0];
110877               return null
110878             }
110879             var left = cwLeft;
110880             var right = cwRight;
110881             if (CGAlgorithms.isCCW(coord)) {
110882               left = cwRight;
110883               right = cwLeft;
110884             }
110885             var e = new Edge(coord, new Label(this._argIndex, Location.BOUNDARY, left, right));
110886             this._lineEdgeMap.put(lr, e);
110887             this.insertEdge(e);
110888             this.insertPoint(this._argIndex, coord[0], Location.BOUNDARY);
110889           };
110890           GeometryGraph.prototype.insertPoint = function insertPoint (argIndex, coord, onLocation) {
110891             var n = this._nodes.addNode(coord);
110892             var lbl = n.getLabel();
110893             if (lbl === null) {
110894               n._label = new Label(argIndex, onLocation);
110895             } else { lbl.setLocation(argIndex, onLocation); }
110896           };
110897           GeometryGraph.prototype.createEdgeSetIntersector = function createEdgeSetIntersector () {
110898             return new SimpleMCSweepLineIntersector()
110899           };
110900           GeometryGraph.prototype.addSelfIntersectionNodes = function addSelfIntersectionNodes (argIndex) {
110901             var this$1 = this;
110902
110903             for (var i = this._edges.iterator(); i.hasNext();) {
110904               var e = i.next();
110905               var eLoc = e.getLabel().getLocation(argIndex);
110906               for (var eiIt = e.eiList.iterator(); eiIt.hasNext();) {
110907                 var ei = eiIt.next();
110908                 this$1.addSelfIntersectionNode(argIndex, ei.coord, eLoc);
110909               }
110910             }
110911           };
110912           GeometryGraph.prototype.add = function add () {
110913             if (arguments.length === 1) {
110914               var g = arguments[0];
110915               if (g.isEmpty()) { return null }
110916               if (g instanceof MultiPolygon) { this._useBoundaryDeterminationRule = false; }
110917               if (g instanceof Polygon) { this.addPolygon(g); }
110918               else if (g instanceof LineString) { this.addLineString(g); }
110919               else if (g instanceof Point$1) { this.addPoint(g); }
110920               else if (g instanceof MultiPoint) { this.addCollection(g); }
110921               else if (g instanceof MultiLineString) { this.addCollection(g); }
110922               else if (g instanceof MultiPolygon) { this.addCollection(g); }
110923               else if (g instanceof GeometryCollection) { this.addCollection(g); }
110924               else { throw new Error(g.getClass().getName()) }
110925             } else { return PlanarGraph$$1.prototype.add.apply(this, arguments) }
110926           };
110927           GeometryGraph.prototype.addCollection = function addCollection (gc) {
110928             var this$1 = this;
110929
110930             for (var i = 0; i < gc.getNumGeometries(); i++) {
110931               var g = gc.getGeometryN(i);
110932               this$1.add(g);
110933             }
110934           };
110935           GeometryGraph.prototype.locate = function locate (pt) {
110936             if (hasInterface(this._parentGeom, Polygonal) && this._parentGeom.getNumGeometries() > 50) {
110937               if (this._areaPtLocator === null) {
110938                 this._areaPtLocator = new IndexedPointInAreaLocator(this._parentGeom);
110939               }
110940               return this._areaPtLocator.locate(pt)
110941             }
110942             return this._ptLocator.locate(pt, this._parentGeom)
110943           };
110944           GeometryGraph.prototype.findEdge = function findEdge () {
110945             if (arguments.length === 1) {
110946               var line = arguments[0];
110947               return this._lineEdgeMap.get(line)
110948             } else { return PlanarGraph$$1.prototype.findEdge.apply(this, arguments) }
110949           };
110950           GeometryGraph.prototype.interfaces_ = function interfaces_ () {
110951             return []
110952           };
110953           GeometryGraph.prototype.getClass = function getClass () {
110954             return GeometryGraph
110955           };
110956           GeometryGraph.determineBoundary = function determineBoundary (boundaryNodeRule, boundaryCount) {
110957             return boundaryNodeRule.isInBoundary(boundaryCount) ? Location.BOUNDARY : Location.INTERIOR
110958           };
110959
110960           return GeometryGraph;
110961         }(PlanarGraph));
110962
110963         var GeometryGraphOp = function GeometryGraphOp () {
110964           this._li = new RobustLineIntersector();
110965           this._resultPrecisionModel = null;
110966           this._arg = null;
110967           if (arguments.length === 1) {
110968             var g0 = arguments[0];
110969             this.setComputationPrecision(g0.getPrecisionModel());
110970             this._arg = new Array(1).fill(null);
110971             this._arg[0] = new GeometryGraph(0, g0);
110972           } else if (arguments.length === 2) {
110973             var g0$1 = arguments[0];
110974             var g1 = arguments[1];
110975             var boundaryNodeRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
110976             if (g0$1.getPrecisionModel().compareTo(g1.getPrecisionModel()) >= 0) { this.setComputationPrecision(g0$1.getPrecisionModel()); } else { this.setComputationPrecision(g1.getPrecisionModel()); }
110977             this._arg = new Array(2).fill(null);
110978             this._arg[0] = new GeometryGraph(0, g0$1, boundaryNodeRule);
110979             this._arg[1] = new GeometryGraph(1, g1, boundaryNodeRule);
110980           } else if (arguments.length === 3) {
110981             var g0$2 = arguments[0];
110982             var g1$1 = arguments[1];
110983             var boundaryNodeRule$1 = arguments[2];
110984             if (g0$2.getPrecisionModel().compareTo(g1$1.getPrecisionModel()) >= 0) { this.setComputationPrecision(g0$2.getPrecisionModel()); } else { this.setComputationPrecision(g1$1.getPrecisionModel()); }
110985             this._arg = new Array(2).fill(null);
110986             this._arg[0] = new GeometryGraph(0, g0$2, boundaryNodeRule$1);
110987             this._arg[1] = new GeometryGraph(1, g1$1, boundaryNodeRule$1);
110988           }
110989         };
110990         GeometryGraphOp.prototype.getArgGeometry = function getArgGeometry (i) {
110991           return this._arg[i].getGeometry()
110992         };
110993         GeometryGraphOp.prototype.setComputationPrecision = function setComputationPrecision (pm) {
110994           this._resultPrecisionModel = pm;
110995           this._li.setPrecisionModel(this._resultPrecisionModel);
110996         };
110997         GeometryGraphOp.prototype.interfaces_ = function interfaces_ () {
110998           return []
110999         };
111000         GeometryGraphOp.prototype.getClass = function getClass () {
111001           return GeometryGraphOp
111002         };
111003
111004         // operation.geometrygraph
111005
111006         var GeometryMapper = function GeometryMapper () {};
111007
111008         GeometryMapper.prototype.interfaces_ = function interfaces_ () {
111009           return []
111010         };
111011         GeometryMapper.prototype.getClass = function getClass () {
111012           return GeometryMapper
111013         };
111014         GeometryMapper.map = function map () {
111015           if (arguments[0] instanceof Geometry && hasInterface(arguments[1], GeometryMapper.MapOp)) {
111016             var geom = arguments[0];
111017             var op = arguments[1];
111018             var mapped = new ArrayList();
111019             for (var i = 0; i < geom.getNumGeometries(); i++) {
111020               var g = op.map(geom.getGeometryN(i));
111021               if (g !== null) { mapped.add(g); }
111022             }
111023             return geom.getFactory().buildGeometry(mapped)
111024           } else if (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], GeometryMapper.MapOp)) {
111025             var geoms = arguments[0];
111026             var op$1 = arguments[1];
111027             var mapped$1 = new ArrayList();
111028             for (var i$1 = geoms.iterator(); i$1.hasNext();) {
111029               var g$1 = i$1.next();
111030               var gr = op$1.map(g$1);
111031               if (gr !== null) { mapped$1.add(gr); }
111032             }
111033             return mapped$1
111034           }
111035         };
111036         GeometryMapper.MapOp = function MapOp () {};
111037
111038         var OverlayOp = (function (GeometryGraphOp) {
111039           function OverlayOp () {
111040             var g0 = arguments[0];
111041             var g1 = arguments[1];
111042             GeometryGraphOp.call(this, g0, g1);
111043             this._ptLocator = new PointLocator();
111044             this._geomFact = null;
111045             this._resultGeom = null;
111046             this._graph = null;
111047             this._edgeList = new EdgeList();
111048             this._resultPolyList = new ArrayList();
111049             this._resultLineList = new ArrayList();
111050             this._resultPointList = new ArrayList();
111051             this._graph = new PlanarGraph(new OverlayNodeFactory());
111052             this._geomFact = g0.getFactory();
111053           }
111054
111055           if ( GeometryGraphOp ) OverlayOp.__proto__ = GeometryGraphOp;
111056           OverlayOp.prototype = Object.create( GeometryGraphOp && GeometryGraphOp.prototype );
111057           OverlayOp.prototype.constructor = OverlayOp;
111058           OverlayOp.prototype.insertUniqueEdge = function insertUniqueEdge (e) {
111059             var existingEdge = this._edgeList.findEqualEdge(e);
111060             if (existingEdge !== null) {
111061               var existingLabel = existingEdge.getLabel();
111062               var labelToMerge = e.getLabel();
111063               if (!existingEdge.isPointwiseEqual(e)) {
111064                 labelToMerge = new Label(e.getLabel());
111065                 labelToMerge.flip();
111066               }
111067               var depth = existingEdge.getDepth();
111068               if (depth.isNull()) {
111069                 depth.add(existingLabel);
111070               }
111071               depth.add(labelToMerge);
111072               existingLabel.merge(labelToMerge);
111073             } else {
111074               this._edgeList.add(e);
111075             }
111076           };
111077           OverlayOp.prototype.getGraph = function getGraph () {
111078             return this._graph
111079           };
111080           OverlayOp.prototype.cancelDuplicateResultEdges = function cancelDuplicateResultEdges () {
111081             for (var it = this._graph.getEdgeEnds().iterator(); it.hasNext();) {
111082               var de = it.next();
111083               var sym = de.getSym();
111084               if (de.isInResult() && sym.isInResult()) {
111085                 de.setInResult(false);
111086                 sym.setInResult(false);
111087               }
111088             }
111089           };
111090           OverlayOp.prototype.isCoveredByLA = function isCoveredByLA (coord) {
111091             if (this.isCovered(coord, this._resultLineList)) { return true }
111092             if (this.isCovered(coord, this._resultPolyList)) { return true }
111093             return false
111094           };
111095           OverlayOp.prototype.computeGeometry = function computeGeometry (resultPointList, resultLineList, resultPolyList, opcode) {
111096             var geomList = new ArrayList();
111097             geomList.addAll(resultPointList);
111098             geomList.addAll(resultLineList);
111099             geomList.addAll(resultPolyList);
111100             if (geomList.isEmpty()) { return OverlayOp.createEmptyResult(opcode, this._arg[0].getGeometry(), this._arg[1].getGeometry(), this._geomFact) }
111101             return this._geomFact.buildGeometry(geomList)
111102           };
111103           OverlayOp.prototype.mergeSymLabels = function mergeSymLabels () {
111104             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111105               var node = nodeit.next();
111106               node.getEdges().mergeSymLabels();
111107             }
111108           };
111109           OverlayOp.prototype.isCovered = function isCovered (coord, geomList) {
111110             var this$1 = this;
111111
111112             for (var it = geomList.iterator(); it.hasNext();) {
111113               var geom = it.next();
111114               var loc = this$1._ptLocator.locate(coord, geom);
111115               if (loc !== Location.EXTERIOR) { return true }
111116             }
111117             return false
111118           };
111119           OverlayOp.prototype.replaceCollapsedEdges = function replaceCollapsedEdges () {
111120             var newEdges = new ArrayList();
111121             for (var it = this._edgeList.iterator(); it.hasNext();) {
111122               var e = it.next();
111123               if (e.isCollapsed()) {
111124                 it.remove();
111125                 newEdges.add(e.getCollapsedEdge());
111126               }
111127             }
111128             this._edgeList.addAll(newEdges);
111129           };
111130           OverlayOp.prototype.updateNodeLabelling = function updateNodeLabelling () {
111131             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111132               var node = nodeit.next();
111133               var lbl = node.getEdges().getLabel();
111134               node.getLabel().merge(lbl);
111135             }
111136           };
111137           OverlayOp.prototype.getResultGeometry = function getResultGeometry (overlayOpCode) {
111138             this.computeOverlay(overlayOpCode);
111139             return this._resultGeom
111140           };
111141           OverlayOp.prototype.insertUniqueEdges = function insertUniqueEdges (edges) {
111142             var this$1 = this;
111143
111144             for (var i = edges.iterator(); i.hasNext();) {
111145               var e = i.next();
111146               this$1.insertUniqueEdge(e);
111147             }
111148           };
111149           OverlayOp.prototype.computeOverlay = function computeOverlay (opCode) {
111150             this.copyPoints(0);
111151             this.copyPoints(1);
111152             this._arg[0].computeSelfNodes(this._li, false);
111153             this._arg[1].computeSelfNodes(this._li, false);
111154             this._arg[0].computeEdgeIntersections(this._arg[1], this._li, true);
111155             var baseSplitEdges = new ArrayList();
111156             this._arg[0].computeSplitEdges(baseSplitEdges);
111157             this._arg[1].computeSplitEdges(baseSplitEdges);
111158             // const splitEdges = baseSplitEdges
111159             this.insertUniqueEdges(baseSplitEdges);
111160             this.computeLabelsFromDepths();
111161             this.replaceCollapsedEdges();
111162             EdgeNodingValidator.checkValid(this._edgeList.getEdges());
111163             this._graph.addEdges(this._edgeList.getEdges());
111164             this.computeLabelling();
111165             this.labelIncompleteNodes();
111166             this.findResultAreaEdges(opCode);
111167             this.cancelDuplicateResultEdges();
111168             var polyBuilder = new PolygonBuilder(this._geomFact);
111169             polyBuilder.add(this._graph);
111170             this._resultPolyList = polyBuilder.getPolygons();
111171             var lineBuilder = new LineBuilder(this, this._geomFact, this._ptLocator);
111172             this._resultLineList = lineBuilder.build(opCode);
111173             var pointBuilder = new PointBuilder(this, this._geomFact, this._ptLocator);
111174             this._resultPointList = pointBuilder.build(opCode);
111175             this._resultGeom = this.computeGeometry(this._resultPointList, this._resultLineList, this._resultPolyList, opCode);
111176           };
111177           OverlayOp.prototype.labelIncompleteNode = function labelIncompleteNode (n, targetIndex) {
111178             var loc = this._ptLocator.locate(n.getCoordinate(), this._arg[targetIndex].getGeometry());
111179             n.getLabel().setLocation(targetIndex, loc);
111180           };
111181           OverlayOp.prototype.copyPoints = function copyPoints (argIndex) {
111182             var this$1 = this;
111183
111184             for (var i = this._arg[argIndex].getNodeIterator(); i.hasNext();) {
111185               var graphNode = i.next();
111186               var newNode = this$1._graph.addNode(graphNode.getCoordinate());
111187               newNode.setLabel(argIndex, graphNode.getLabel().getLocation(argIndex));
111188             }
111189           };
111190           OverlayOp.prototype.findResultAreaEdges = function findResultAreaEdges (opCode) {
111191             for (var it = this._graph.getEdgeEnds().iterator(); it.hasNext();) {
111192               var de = it.next();
111193               var label = de.getLabel();
111194               if (label.isArea() && !de.isInteriorAreaEdge() && OverlayOp.isResultOfOp(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), opCode)) {
111195                 de.setInResult(true);
111196               }
111197             }
111198           };
111199           OverlayOp.prototype.computeLabelsFromDepths = function computeLabelsFromDepths () {
111200             for (var it = this._edgeList.iterator(); it.hasNext();) {
111201               var e = it.next();
111202               var lbl = e.getLabel();
111203               var depth = e.getDepth();
111204               if (!depth.isNull()) {
111205                 depth.normalize();
111206                 for (var i = 0; i < 2; i++) {
111207                   if (!lbl.isNull(i) && lbl.isArea() && !depth.isNull(i)) {
111208                     if (depth.getDelta(i) === 0) {
111209                       lbl.toLine(i);
111210                     } else {
111211                       Assert.isTrue(!depth.isNull(i, Position.LEFT), 'depth of LEFT side has not been initialized');
111212                       lbl.setLocation(i, Position.LEFT, depth.getLocation(i, Position.LEFT));
111213                       Assert.isTrue(!depth.isNull(i, Position.RIGHT), 'depth of RIGHT side has not been initialized');
111214                       lbl.setLocation(i, Position.RIGHT, depth.getLocation(i, Position.RIGHT));
111215                     }
111216                   }
111217                 }
111218               }
111219             }
111220           };
111221           OverlayOp.prototype.computeLabelling = function computeLabelling () {
111222             var this$1 = this;
111223
111224             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111225               var node = nodeit.next();
111226               node.getEdges().computeLabelling(this$1._arg);
111227             }
111228             this.mergeSymLabels();
111229             this.updateNodeLabelling();
111230           };
111231           OverlayOp.prototype.labelIncompleteNodes = function labelIncompleteNodes () {
111232             var this$1 = this;
111233
111234             // let nodeCount = 0
111235             for (var ni = this._graph.getNodes().iterator(); ni.hasNext();) {
111236               var n = ni.next();
111237               var label = n.getLabel();
111238               if (n.isIsolated()) {
111239                 // nodeCount++
111240                 if (label.isNull(0)) { this$1.labelIncompleteNode(n, 0); } else { this$1.labelIncompleteNode(n, 1); }
111241               }
111242               n.getEdges().updateLabelling(label);
111243             }
111244           };
111245           OverlayOp.prototype.isCoveredByA = function isCoveredByA (coord) {
111246             if (this.isCovered(coord, this._resultPolyList)) { return true }
111247             return false
111248           };
111249           OverlayOp.prototype.interfaces_ = function interfaces_ () {
111250             return []
111251           };
111252           OverlayOp.prototype.getClass = function getClass () {
111253             return OverlayOp
111254           };
111255
111256           return OverlayOp;
111257         }(GeometryGraphOp));
111258
111259         OverlayOp.overlayOp = function (geom0, geom1, opCode) {
111260           var gov = new OverlayOp(geom0, geom1);
111261           var geomOv = gov.getResultGeometry(opCode);
111262           return geomOv
111263         };
111264         OverlayOp.intersection = function (g, other) {
111265           if (g.isEmpty() || other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.INTERSECTION, g, other, g.getFactory()) }
111266           if (g.isGeometryCollection()) {
111267             var g2 = other;
111268             return GeometryCollectionMapper.map(g, {
111269               interfaces_: function () {
111270                 return [GeometryMapper.MapOp]
111271               },
111272               map: function (g) {
111273                 return g.intersection(g2)
111274               }
111275             })
111276           }
111277           g.checkNotGeometryCollection(g);
111278           g.checkNotGeometryCollection(other);
111279           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.INTERSECTION)
111280         };
111281         OverlayOp.symDifference = function (g, other) {
111282           if (g.isEmpty() || other.isEmpty()) {
111283             if (g.isEmpty() && other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.SYMDIFFERENCE, g, other, g.getFactory()) }
111284             if (g.isEmpty()) { return other.copy() }
111285             if (other.isEmpty()) { return g.copy() }
111286           }
111287           g.checkNotGeometryCollection(g);
111288           g.checkNotGeometryCollection(other);
111289           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.SYMDIFFERENCE)
111290         };
111291         OverlayOp.resultDimension = function (opCode, g0, g1) {
111292           var dim0 = g0.getDimension();
111293           var dim1 = g1.getDimension();
111294           var resultDimension = -1;
111295           switch (opCode) {
111296             case OverlayOp.INTERSECTION:
111297               resultDimension = Math.min(dim0, dim1);
111298               break
111299             case OverlayOp.UNION:
111300               resultDimension = Math.max(dim0, dim1);
111301               break
111302             case OverlayOp.DIFFERENCE:
111303               resultDimension = dim0;
111304               break
111305             case OverlayOp.SYMDIFFERENCE:
111306               resultDimension = Math.max(dim0, dim1);
111307               break
111308           }
111309           return resultDimension
111310         };
111311         OverlayOp.createEmptyResult = function (overlayOpCode, a, b, geomFact) {
111312           var result = null;
111313           switch (OverlayOp.resultDimension(overlayOpCode, a, b)) {
111314             case -1:
111315               result = geomFact.createGeometryCollection(new Array(0).fill(null));
111316               break
111317             case 0:
111318               result = geomFact.createPoint();
111319               break
111320             case 1:
111321               result = geomFact.createLineString();
111322               break
111323             case 2:
111324               result = geomFact.createPolygon();
111325               break
111326           }
111327           return result
111328         };
111329         OverlayOp.difference = function (g, other) {
111330           if (g.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.DIFFERENCE, g, other, g.getFactory()) }
111331           if (other.isEmpty()) { return g.copy() }
111332           g.checkNotGeometryCollection(g);
111333           g.checkNotGeometryCollection(other);
111334           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.DIFFERENCE)
111335         };
111336         OverlayOp.isResultOfOp = function () {
111337           if (arguments.length === 2) {
111338             var label = arguments[0];
111339             var opCode = arguments[1];
111340             var loc0 = label.getLocation(0);
111341             var loc1 = label.getLocation(1);
111342             return OverlayOp.isResultOfOp(loc0, loc1, opCode)
111343           } else if (arguments.length === 3) {
111344             var loc0$1 = arguments[0];
111345             var loc1$1 = arguments[1];
111346             var overlayOpCode = arguments[2];
111347             if (loc0$1 === Location.BOUNDARY) { loc0$1 = Location.INTERIOR; }
111348             if (loc1$1 === Location.BOUNDARY) { loc1$1 = Location.INTERIOR; }
111349             switch (overlayOpCode) {
111350               case OverlayOp.INTERSECTION:
111351                 return loc0$1 === Location.INTERIOR && loc1$1 === Location.INTERIOR
111352               case OverlayOp.UNION:
111353                 return loc0$1 === Location.INTERIOR || loc1$1 === Location.INTERIOR
111354               case OverlayOp.DIFFERENCE:
111355                 return loc0$1 === Location.INTERIOR && loc1$1 !== Location.INTERIOR
111356               case OverlayOp.SYMDIFFERENCE:
111357                 return (loc0$1 === Location.INTERIOR && loc1$1 !== Location.INTERIOR) || (loc0$1 !== Location.INTERIOR && loc1$1 === Location.INTERIOR)
111358             }
111359             return false
111360           }
111361         };
111362         OverlayOp.INTERSECTION = 1;
111363         OverlayOp.UNION = 2;
111364         OverlayOp.DIFFERENCE = 3;
111365         OverlayOp.SYMDIFFERENCE = 4;
111366
111367         var FuzzyPointLocator = function FuzzyPointLocator () {
111368           this._g = null;
111369           this._boundaryDistanceTolerance = null;
111370           this._linework = null;
111371           this._ptLocator = new PointLocator();
111372           this._seg = new LineSegment();
111373           var g = arguments[0];
111374           var boundaryDistanceTolerance = arguments[1];
111375           this._g = g;
111376           this._boundaryDistanceTolerance = boundaryDistanceTolerance;
111377           this._linework = this.extractLinework(g);
111378         };
111379         FuzzyPointLocator.prototype.isWithinToleranceOfBoundary = function isWithinToleranceOfBoundary (pt) {
111380             var this$1 = this;
111381
111382           for (var i = 0; i < this._linework.getNumGeometries(); i++) {
111383             var line = this$1._linework.getGeometryN(i);
111384             var seq = line.getCoordinateSequence();
111385             for (var j = 0; j < seq.size() - 1; j++) {
111386               seq.getCoordinate(j, this$1._seg.p0);
111387               seq.getCoordinate(j + 1, this$1._seg.p1);
111388               var dist = this$1._seg.distance(pt);
111389               if (dist <= this$1._boundaryDistanceTolerance) { return true }
111390             }
111391           }
111392           return false
111393         };
111394         FuzzyPointLocator.prototype.getLocation = function getLocation (pt) {
111395           if (this.isWithinToleranceOfBoundary(pt)) { return Location.BOUNDARY }
111396           return this._ptLocator.locate(pt, this._g)
111397         };
111398         FuzzyPointLocator.prototype.extractLinework = function extractLinework (g) {
111399           var extracter = new PolygonalLineworkExtracter();
111400           g.apply(extracter);
111401           var linework = extracter.getLinework();
111402           var lines = GeometryFactory.toLineStringArray(linework);
111403           return g.getFactory().createMultiLineString(lines)
111404         };
111405         FuzzyPointLocator.prototype.interfaces_ = function interfaces_ () {
111406           return []
111407         };
111408         FuzzyPointLocator.prototype.getClass = function getClass () {
111409           return FuzzyPointLocator
111410         };
111411
111412         var PolygonalLineworkExtracter = function PolygonalLineworkExtracter () {
111413           this._linework = null;
111414           this._linework = new ArrayList();
111415         };
111416         PolygonalLineworkExtracter.prototype.getLinework = function getLinework () {
111417           return this._linework
111418         };
111419         PolygonalLineworkExtracter.prototype.filter = function filter (g) {
111420             var this$1 = this;
111421
111422           if (g instanceof Polygon) {
111423             var poly = g;
111424             this._linework.add(poly.getExteriorRing());
111425             for (var i = 0; i < poly.getNumInteriorRing(); i++) {
111426               this$1._linework.add(poly.getInteriorRingN(i));
111427             }
111428           }
111429         };
111430         PolygonalLineworkExtracter.prototype.interfaces_ = function interfaces_ () {
111431           return [GeometryFilter]
111432         };
111433         PolygonalLineworkExtracter.prototype.getClass = function getClass () {
111434           return PolygonalLineworkExtracter
111435         };
111436
111437         var OffsetPointGenerator = function OffsetPointGenerator () {
111438           this._g = null;
111439           this._doLeft = true;
111440           this._doRight = true;
111441           var g = arguments[0];
111442           this._g = g;
111443         };
111444         OffsetPointGenerator.prototype.extractPoints = function extractPoints (line, offsetDistance, offsetPts) {
111445             var this$1 = this;
111446
111447           var pts = line.getCoordinates();
111448           for (var i = 0; i < pts.length - 1; i++) {
111449             this$1.computeOffsetPoints(pts[i], pts[i + 1], offsetDistance, offsetPts);
111450           }
111451         };
111452         OffsetPointGenerator.prototype.setSidesToGenerate = function setSidesToGenerate (doLeft, doRight) {
111453           this._doLeft = doLeft;
111454           this._doRight = doRight;
111455         };
111456         OffsetPointGenerator.prototype.getPoints = function getPoints (offsetDistance) {
111457             var this$1 = this;
111458
111459           var offsetPts = new ArrayList();
111460           var lines = LinearComponentExtracter.getLines(this._g);
111461           for (var i = lines.iterator(); i.hasNext();) {
111462             var line = i.next();
111463             this$1.extractPoints(line, offsetDistance, offsetPts);
111464           }
111465           return offsetPts
111466         };
111467         OffsetPointGenerator.prototype.computeOffsetPoints = function computeOffsetPoints (p0, p1, offsetDistance, offsetPts) {
111468           var dx = p1.x - p0.x;
111469           var dy = p1.y - p0.y;
111470           var len = Math.sqrt(dx * dx + dy * dy);
111471           var ux = offsetDistance * dx / len;
111472           var uy = offsetDistance * dy / len;
111473           var midX = (p1.x + p0.x) / 2;
111474           var midY = (p1.y + p0.y) / 2;
111475           if (this._doLeft) {
111476             var offsetLeft = new Coordinate(midX - uy, midY + ux);
111477             offsetPts.add(offsetLeft);
111478           }
111479           if (this._doRight) {
111480             var offsetRight = new Coordinate(midX + uy, midY - ux);
111481             offsetPts.add(offsetRight);
111482           }
111483         };
111484         OffsetPointGenerator.prototype.interfaces_ = function interfaces_ () {
111485           return []
111486         };
111487         OffsetPointGenerator.prototype.getClass = function getClass () {
111488           return OffsetPointGenerator
111489         };
111490
111491         var OverlayResultValidator = function OverlayResultValidator () {
111492           this._geom = null;
111493           this._locFinder = null;
111494           this._location = new Array(3).fill(null);
111495           this._invalidLocation = null;
111496           this._boundaryDistanceTolerance = OverlayResultValidator.TOLERANCE;
111497           this._testCoords = new ArrayList();
111498           var a = arguments[0];
111499           var b = arguments[1];
111500           var result = arguments[2];
111501           this._boundaryDistanceTolerance = OverlayResultValidator.computeBoundaryDistanceTolerance(a, b);
111502           this._geom = [a, b, result];
111503           this._locFinder = [new FuzzyPointLocator(this._geom[0], this._boundaryDistanceTolerance), new FuzzyPointLocator(this._geom[1], this._boundaryDistanceTolerance), new FuzzyPointLocator(this._geom[2], this._boundaryDistanceTolerance)];
111504         };
111505
111506         var staticAccessors$46 = { TOLERANCE: { configurable: true } };
111507         OverlayResultValidator.prototype.reportResult = function reportResult (overlayOp, location, expectedInterior) {
111508           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]));
111509         };
111510         OverlayResultValidator.prototype.isValid = function isValid (overlayOp) {
111511           this.addTestPts(this._geom[0]);
111512           this.addTestPts(this._geom[1]);
111513           var isValid = this.checkValid(overlayOp);
111514           return isValid
111515         };
111516         OverlayResultValidator.prototype.checkValid = function checkValid () {
111517             var this$1 = this;
111518
111519           if (arguments.length === 1) {
111520             var overlayOp = arguments[0];
111521             for (var i = 0; i < this._testCoords.size(); i++) {
111522               var pt = this$1._testCoords.get(i);
111523               if (!this$1.checkValid(overlayOp, pt)) {
111524                 this$1._invalidLocation = pt;
111525                 return false
111526               }
111527             }
111528             return true
111529           } else if (arguments.length === 2) {
111530             var overlayOp$1 = arguments[0];
111531             var pt$1 = arguments[1];
111532             this._location[0] = this._locFinder[0].getLocation(pt$1);
111533             this._location[1] = this._locFinder[1].getLocation(pt$1);
111534             this._location[2] = this._locFinder[2].getLocation(pt$1);
111535             if (OverlayResultValidator.hasLocation(this._location, Location.BOUNDARY)) { return true }
111536             return this.isValidResult(overlayOp$1, this._location)
111537           }
111538         };
111539         OverlayResultValidator.prototype.addTestPts = function addTestPts (g) {
111540           var ptGen = new OffsetPointGenerator(g);
111541           this._testCoords.addAll(ptGen.getPoints(5 * this._boundaryDistanceTolerance));
111542         };
111543         OverlayResultValidator.prototype.isValidResult = function isValidResult (overlayOp, location) {
111544           var expectedInterior = OverlayOp.isResultOfOp(location[0], location[1], overlayOp);
111545           var resultInInterior = location[2] === Location.INTERIOR;
111546           var isValid = !(expectedInterior ^ resultInInterior);
111547           if (!isValid) { this.reportResult(overlayOp, location, expectedInterior); }
111548           return isValid
111549         };
111550         OverlayResultValidator.prototype.getInvalidLocation = function getInvalidLocation () {
111551           return this._invalidLocation
111552         };
111553         OverlayResultValidator.prototype.interfaces_ = function interfaces_ () {
111554           return []
111555         };
111556         OverlayResultValidator.prototype.getClass = function getClass () {
111557           return OverlayResultValidator
111558         };
111559         OverlayResultValidator.hasLocation = function hasLocation (location, loc) {
111560           for (var i = 0; i < 3; i++) {
111561             if (location[i] === loc) { return true }
111562           }
111563           return false
111564         };
111565         OverlayResultValidator.computeBoundaryDistanceTolerance = function computeBoundaryDistanceTolerance (g0, g1) {
111566           return Math.min(GeometrySnapper.computeSizeBasedSnapTolerance(g0), GeometrySnapper.computeSizeBasedSnapTolerance(g1))
111567         };
111568         OverlayResultValidator.isValid = function isValid (a, b, overlayOp, result) {
111569           var validator = new OverlayResultValidator(a, b, result);
111570           return validator.isValid(overlayOp)
111571         };
111572         staticAccessors$46.TOLERANCE.get = function () { return 0.000001 };
111573
111574         Object.defineProperties( OverlayResultValidator, staticAccessors$46 );
111575
111576         // operation.overlay
111577
111578         var GeometryCombiner = function GeometryCombiner (geoms) {
111579           this._geomFactory = null;
111580           this._skipEmpty = false;
111581           this._inputGeoms = null;
111582           this._geomFactory = GeometryCombiner.extractFactory(geoms);
111583           this._inputGeoms = geoms;
111584         };
111585         GeometryCombiner.prototype.extractElements = function extractElements (geom, elems) {
111586             var this$1 = this;
111587
111588           if (geom === null) { return null }
111589           for (var i = 0; i < geom.getNumGeometries(); i++) {
111590             var elemGeom = geom.getGeometryN(i);
111591             if (this$1._skipEmpty && elemGeom.isEmpty()) { continue }
111592             elems.add(elemGeom);
111593           }
111594         };
111595         GeometryCombiner.prototype.combine = function combine () {
111596             var this$1 = this;
111597
111598           var elems = new ArrayList();
111599           for (var i = this._inputGeoms.iterator(); i.hasNext();) {
111600             var g = i.next();
111601             this$1.extractElements(g, elems);
111602           }
111603           if (elems.size() === 0) {
111604             if (this._geomFactory !== null) {
111605               return this._geomFactory.createGeometryCollection(null)
111606             }
111607             return null
111608           }
111609           return this._geomFactory.buildGeometry(elems)
111610         };
111611         GeometryCombiner.prototype.interfaces_ = function interfaces_ () {
111612           return []
111613         };
111614         GeometryCombiner.prototype.getClass = function getClass () {
111615           return GeometryCombiner
111616         };
111617         GeometryCombiner.combine = function combine () {
111618           if (arguments.length === 1) {
111619             var geoms = arguments[0];
111620             var combiner = new GeometryCombiner(geoms);
111621             return combiner.combine()
111622           } else if (arguments.length === 2) {
111623             var g0 = arguments[0];
111624             var g1 = arguments[1];
111625             var combiner$1 = new GeometryCombiner(GeometryCombiner.createList(g0, g1));
111626             return combiner$1.combine()
111627           } else if (arguments.length === 3) {
111628             var g0$1 = arguments[0];
111629             var g1$1 = arguments[1];
111630             var g2 = arguments[2];
111631             var combiner$2 = new GeometryCombiner(GeometryCombiner.createList(g0$1, g1$1, g2));
111632             return combiner$2.combine()
111633           }
111634         };
111635         GeometryCombiner.extractFactory = function extractFactory (geoms) {
111636           if (geoms.isEmpty()) { return null }
111637           return geoms.iterator().next().getFactory()
111638         };
111639         GeometryCombiner.createList = function createList () {
111640           if (arguments.length === 2) {
111641             var obj0 = arguments[0];
111642             var obj1 = arguments[1];
111643             var list = new ArrayList();
111644             list.add(obj0);
111645             list.add(obj1);
111646             return list
111647           } else if (arguments.length === 3) {
111648             var obj0$1 = arguments[0];
111649             var obj1$1 = arguments[1];
111650             var obj2 = arguments[2];
111651             var list$1 = new ArrayList();
111652             list$1.add(obj0$1);
111653             list$1.add(obj1$1);
111654             list$1.add(obj2);
111655             return list$1
111656           }
111657         };
111658
111659         var CascadedPolygonUnion = function CascadedPolygonUnion () {
111660           this._inputPolys = null;
111661           this._geomFactory = null;
111662           var polys = arguments[0];
111663           this._inputPolys = polys;
111664           if (this._inputPolys === null) { this._inputPolys = new ArrayList(); }
111665         };
111666
111667         var staticAccessors$47 = { STRTREE_NODE_CAPACITY: { configurable: true } };
111668         CascadedPolygonUnion.prototype.reduceToGeometries = function reduceToGeometries (geomTree) {
111669             var this$1 = this;
111670
111671           var geoms = new ArrayList();
111672           for (var i = geomTree.iterator(); i.hasNext();) {
111673             var o = i.next();
111674             var geom = null;
111675             if (hasInterface(o, List)) {
111676               geom = this$1.unionTree(o);
111677             } else if (o instanceof Geometry) {
111678               geom = o;
111679             }
111680             geoms.add(geom);
111681           }
111682           return geoms
111683         };
111684         CascadedPolygonUnion.prototype.extractByEnvelope = function extractByEnvelope (env, geom, disjointGeoms) {
111685           var intersectingGeoms = new ArrayList();
111686           for (var i = 0; i < geom.getNumGeometries(); i++) {
111687             var elem = geom.getGeometryN(i);
111688             if (elem.getEnvelopeInternal().intersects(env)) { intersectingGeoms.add(elem); } else { disjointGeoms.add(elem); }
111689           }
111690           return this._geomFactory.buildGeometry(intersectingGeoms)
111691         };
111692         CascadedPolygonUnion.prototype.unionOptimized = function unionOptimized (g0, g1) {
111693           var g0Env = g0.getEnvelopeInternal();
111694           var g1Env = g1.getEnvelopeInternal();
111695           if (!g0Env.intersects(g1Env)) {
111696             var combo = GeometryCombiner.combine(g0, g1);
111697             return combo
111698           }
111699           if (g0.getNumGeometries() <= 1 && g1.getNumGeometries() <= 1) { return this.unionActual(g0, g1) }
111700           var commonEnv = g0Env.intersection(g1Env);
111701           return this.unionUsingEnvelopeIntersection(g0, g1, commonEnv)
111702         };
111703         CascadedPolygonUnion.prototype.union = function union () {
111704           if (this._inputPolys === null) { throw new Error('union() method cannot be called twice') }
111705           if (this._inputPolys.isEmpty()) { return null }
111706           this._geomFactory = this._inputPolys.iterator().next().getFactory();
111707           var index = new STRtree(CascadedPolygonUnion.STRTREE_NODE_CAPACITY);
111708           for (var i = this._inputPolys.iterator(); i.hasNext();) {
111709             var item = i.next();
111710             index.insert(item.getEnvelopeInternal(), item);
111711           }
111712           this._inputPolys = null;
111713           var itemTree = index.itemsTree();
111714           var unionAll = this.unionTree(itemTree);
111715           return unionAll
111716         };
111717         CascadedPolygonUnion.prototype.binaryUnion = function binaryUnion () {
111718           if (arguments.length === 1) {
111719             var geoms = arguments[0];
111720             return this.binaryUnion(geoms, 0, geoms.size())
111721           } else if (arguments.length === 3) {
111722             var geoms$1 = arguments[0];
111723             var start = arguments[1];
111724             var end = arguments[2];
111725             if (end - start <= 1) {
111726               var g0 = CascadedPolygonUnion.getGeometry(geoms$1, start);
111727               return this.unionSafe(g0, null)
111728             } else if (end - start === 2) {
111729               return this.unionSafe(CascadedPolygonUnion.getGeometry(geoms$1, start), CascadedPolygonUnion.getGeometry(geoms$1, start + 1))
111730             } else {
111731               var mid = Math.trunc((end + start) / 2);
111732               var g0$1 = this.binaryUnion(geoms$1, start, mid);
111733               var g1 = this.binaryUnion(geoms$1, mid, end);
111734               return this.unionSafe(g0$1, g1)
111735             }
111736           }
111737         };
111738         CascadedPolygonUnion.prototype.repeatedUnion = function repeatedUnion (geoms) {
111739           var union = null;
111740           for (var i = geoms.iterator(); i.hasNext();) {
111741             var g = i.next();
111742             if (union === null) { union = g.copy(); } else { union = union.union(g); }
111743           }
111744           return union
111745         };
111746         CascadedPolygonUnion.prototype.unionSafe = function unionSafe (g0, g1) {
111747           if (g0 === null && g1 === null) { return null }
111748           if (g0 === null) { return g1.copy() }
111749           if (g1 === null) { return g0.copy() }
111750           return this.unionOptimized(g0, g1)
111751         };
111752         CascadedPolygonUnion.prototype.unionActual = function unionActual (g0, g1) {
111753           return CascadedPolygonUnion.restrictToPolygons(g0.union(g1))
111754         };
111755         CascadedPolygonUnion.prototype.unionTree = function unionTree (geomTree) {
111756           var geoms = this.reduceToGeometries(geomTree);
111757           var union = this.binaryUnion(geoms);
111758           return union
111759         };
111760         CascadedPolygonUnion.prototype.unionUsingEnvelopeIntersection = function unionUsingEnvelopeIntersection (g0, g1, common) {
111761           var disjointPolys = new ArrayList();
111762           var g0Int = this.extractByEnvelope(common, g0, disjointPolys);
111763           var g1Int = this.extractByEnvelope(common, g1, disjointPolys);
111764           var union = this.unionActual(g0Int, g1Int);
111765           disjointPolys.add(union);
111766           var overallUnion = GeometryCombiner.combine(disjointPolys);
111767           return overallUnion
111768         };
111769         CascadedPolygonUnion.prototype.bufferUnion = function bufferUnion () {
111770           if (arguments.length === 1) {
111771             var geoms = arguments[0];
111772             var factory = geoms.get(0).getFactory();
111773             var gColl = factory.buildGeometry(geoms);
111774             var unionAll = gColl.buffer(0.0);
111775             return unionAll
111776           } else if (arguments.length === 2) {
111777             var g0 = arguments[0];
111778             var g1 = arguments[1];
111779             var factory$1 = g0.getFactory();
111780             var gColl$1 = factory$1.createGeometryCollection([g0, g1]);
111781             var unionAll$1 = gColl$1.buffer(0.0);
111782             return unionAll$1
111783           }
111784         };
111785         CascadedPolygonUnion.prototype.interfaces_ = function interfaces_ () {
111786           return []
111787         };
111788         CascadedPolygonUnion.prototype.getClass = function getClass () {
111789           return CascadedPolygonUnion
111790         };
111791         CascadedPolygonUnion.restrictToPolygons = function restrictToPolygons (g) {
111792           if (hasInterface(g, Polygonal)) {
111793             return g
111794           }
111795           var polygons = PolygonExtracter.getPolygons(g);
111796           if (polygons.size() === 1) { return polygons.get(0) }
111797           return g.getFactory().createMultiPolygon(GeometryFactory.toPolygonArray(polygons))
111798         };
111799         CascadedPolygonUnion.getGeometry = function getGeometry (list, index) {
111800           if (index >= list.size()) { return null }
111801           return list.get(index)
111802         };
111803         CascadedPolygonUnion.union = function union (polys) {
111804           var op = new CascadedPolygonUnion(polys);
111805           return op.union()
111806         };
111807         staticAccessors$47.STRTREE_NODE_CAPACITY.get = function () { return 4 };
111808
111809         Object.defineProperties( CascadedPolygonUnion, staticAccessors$47 );
111810
111811         var UnionOp = function UnionOp () {};
111812
111813         UnionOp.prototype.interfaces_ = function interfaces_ () {
111814           return []
111815         };
111816         UnionOp.prototype.getClass = function getClass () {
111817           return UnionOp
111818         };
111819         UnionOp.union = function union (g, other) {
111820           if (g.isEmpty() || other.isEmpty()) {
111821             if (g.isEmpty() && other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.UNION, g, other, g.getFactory()) }
111822             if (g.isEmpty()) { return other.copy() }
111823             if (other.isEmpty()) { return g.copy() }
111824           }
111825           g.checkNotGeometryCollection(g);
111826           g.checkNotGeometryCollection(other);
111827           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.UNION)
111828         };
111829
111830         /**
111831          * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth.
111832          */
111833
111834         /**
111835          * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
111836          *
111837          * @name feature
111838          * @param {Geometry} geometry input geometry
111839          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
111840          * @param {Object} [options={}] Optional Parameters
111841          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
111842          * @param {string|number} [options.id] Identifier associated with the Feature
111843          * @returns {Feature} a GeoJSON Feature
111844          * @example
111845          * var geometry = {
111846          *   "type": "Point",
111847          *   "coordinates": [110, 50]
111848          * };
111849          *
111850          * var feature = turf.feature(geometry);
111851          *
111852          * //=feature
111853          */
111854         function feature$1(geometry, properties, options) {
111855             // Optional Parameters
111856             options = options || {};
111857             if (!isObject$4(options)) throw new Error('options is invalid');
111858             var bbox = options.bbox;
111859             var id = options.id;
111860
111861             // Validation
111862             if (geometry === undefined) throw new Error('geometry is required');
111863             if (properties && properties.constructor !== Object) throw new Error('properties must be an Object');
111864             if (bbox) validateBBox(bbox);
111865             if (id) validateId(id);
111866
111867             // Main
111868             var feat = {type: 'Feature'};
111869             if (id) feat.id = id;
111870             if (bbox) feat.bbox = bbox;
111871             feat.properties = properties || {};
111872             feat.geometry = geometry;
111873             return feat;
111874         }
111875
111876         /**
111877          * isNumber
111878          *
111879          * @param {*} num Number to validate
111880          * @returns {boolean} true/false
111881          * @example
111882          * turf.isNumber(123)
111883          * //=true
111884          * turf.isNumber('foo')
111885          * //=false
111886          */
111887         function isNumber$1(num) {
111888             return !isNaN(num) && num !== null && !Array.isArray(num);
111889         }
111890
111891         /**
111892          * isObject
111893          *
111894          * @param {*} input variable to validate
111895          * @returns {boolean} true/false
111896          * @example
111897          * turf.isObject({elevation: 10})
111898          * //=true
111899          * turf.isObject('foo')
111900          * //=false
111901          */
111902         function isObject$4(input) {
111903             return (!!input) && (input.constructor === Object);
111904         }
111905
111906         /**
111907          * Validate BBox
111908          *
111909          * @private
111910          * @param {Array<number>} bbox BBox to validate
111911          * @returns {void}
111912          * @throws Error if BBox is not valid
111913          * @example
111914          * validateBBox([-180, -40, 110, 50])
111915          * //=OK
111916          * validateBBox([-180, -40])
111917          * //=Error
111918          * validateBBox('Foo')
111919          * //=Error
111920          * validateBBox(5)
111921          * //=Error
111922          * validateBBox(null)
111923          * //=Error
111924          * validateBBox(undefined)
111925          * //=Error
111926          */
111927         function validateBBox(bbox) {
111928             if (!bbox) throw new Error('bbox is required');
111929             if (!Array.isArray(bbox)) throw new Error('bbox must be an Array');
111930             if (bbox.length !== 4 && bbox.length !== 6) throw new Error('bbox must be an Array of 4 or 6 numbers');
111931             bbox.forEach(function (num) {
111932                 if (!isNumber$1(num)) throw new Error('bbox must only contain numbers');
111933             });
111934         }
111935
111936         /**
111937          * Validate Id
111938          *
111939          * @private
111940          * @param {string|number} id Id to validate
111941          * @returns {void}
111942          * @throws Error if Id is not valid
111943          * @example
111944          * validateId([-180, -40, 110, 50])
111945          * //=Error
111946          * validateId([-180, -40])
111947          * //=Error
111948          * validateId('Foo')
111949          * //=OK
111950          * validateId(5)
111951          * //=OK
111952          * validateId(null)
111953          * //=Error
111954          * validateId(undefined)
111955          * //=Error
111956          */
111957         function validateId(id) {
111958             if (!id) throw new Error('id is required');
111959             if (['string', 'number'].indexOf(typeof id) === -1) throw new Error('id must be a number or a string');
111960         }
111961
111962         /**
111963          * Callback for geomEach
111964          *
111965          * @callback geomEachCallback
111966          * @param {Geometry} currentGeometry The current Geometry being processed.
111967          * @param {number} featureIndex The current index of the Feature being processed.
111968          * @param {Object} featureProperties The current Feature Properties being processed.
111969          * @param {Array<number>} featureBBox The current Feature BBox being processed.
111970          * @param {number|string} featureId The current Feature Id being processed.
111971          */
111972
111973         /**
111974          * Iterate over each geometry in any GeoJSON object, similar to Array.forEach()
111975          *
111976          * @name geomEach
111977          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
111978          * @param {Function} callback a method that takes (currentGeometry, featureIndex, featureProperties, featureBBox, featureId)
111979          * @returns {void}
111980          * @example
111981          * var features = turf.featureCollection([
111982          *     turf.point([26, 37], {foo: 'bar'}),
111983          *     turf.point([36, 53], {hello: 'world'})
111984          * ]);
111985          *
111986          * turf.geomEach(features, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
111987          *   //=currentGeometry
111988          *   //=featureIndex
111989          *   //=featureProperties
111990          *   //=featureBBox
111991          *   //=featureId
111992          * });
111993          */
111994         function geomEach(geojson, callback) {
111995             var i, j, g, geometry, stopG,
111996                 geometryMaybeCollection,
111997                 isGeometryCollection,
111998                 featureProperties,
111999                 featureBBox,
112000                 featureId,
112001                 featureIndex = 0,
112002                 isFeatureCollection = geojson.type === 'FeatureCollection',
112003                 isFeature = geojson.type === 'Feature',
112004                 stop = isFeatureCollection ? geojson.features.length : 1;
112005
112006             // This logic may look a little weird. The reason why it is that way
112007             // is because it's trying to be fast. GeoJSON supports multiple kinds
112008             // of objects at its root: FeatureCollection, Features, Geometries.
112009             // This function has the responsibility of handling all of them, and that
112010             // means that some of the `for` loops you see below actually just don't apply
112011             // to certain inputs. For instance, if you give this just a
112012             // Point geometry, then both loops are short-circuited and all we do
112013             // is gradually rename the input until it's called 'geometry'.
112014             //
112015             // This also aims to allocate as few resources as possible: just a
112016             // few numbers and booleans, rather than any temporary arrays as would
112017             // be required with the normalization approach.
112018             for (i = 0; i < stop; i++) {
112019
112020                 geometryMaybeCollection = (isFeatureCollection ? geojson.features[i].geometry :
112021                     (isFeature ? geojson.geometry : geojson));
112022                 featureProperties = (isFeatureCollection ? geojson.features[i].properties :
112023                     (isFeature ? geojson.properties : {}));
112024                 featureBBox = (isFeatureCollection ? geojson.features[i].bbox :
112025                     (isFeature ? geojson.bbox : undefined));
112026                 featureId = (isFeatureCollection ? geojson.features[i].id :
112027                     (isFeature ? geojson.id : undefined));
112028                 isGeometryCollection = (geometryMaybeCollection) ? geometryMaybeCollection.type === 'GeometryCollection' : false;
112029                 stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
112030
112031                 for (g = 0; g < stopG; g++) {
112032                     geometry = isGeometryCollection ?
112033                         geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
112034
112035                     // Handle null Geometry
112036                     if (geometry === null) {
112037                         if (callback(null, featureIndex, featureProperties, featureBBox, featureId) === false) return false;
112038                         continue;
112039                     }
112040                     switch (geometry.type) {
112041                     case 'Point':
112042                     case 'LineString':
112043                     case 'MultiPoint':
112044                     case 'Polygon':
112045                     case 'MultiLineString':
112046                     case 'MultiPolygon': {
112047                         if (callback(geometry, featureIndex, featureProperties, featureBBox, featureId) === false) return false;
112048                         break;
112049                     }
112050                     case 'GeometryCollection': {
112051                         for (j = 0; j < geometry.geometries.length; j++) {
112052                             if (callback(geometry.geometries[j], featureIndex, featureProperties, featureBBox, featureId) === false) return false;
112053                         }
112054                         break;
112055                     }
112056                     default:
112057                         throw new Error('Unknown Geometry Type');
112058                     }
112059                 }
112060                 // Only increase `featureIndex` per each feature
112061                 featureIndex++;
112062             }
112063         }
112064
112065         /**
112066          * Callback for geomReduce
112067          *
112068          * The first time the callback function is called, the values provided as arguments depend
112069          * on whether the reduce method has an initialValue argument.
112070          *
112071          * If an initialValue is provided to the reduce method:
112072          *  - The previousValue argument is initialValue.
112073          *  - The currentValue argument is the value of the first element present in the array.
112074          *
112075          * If an initialValue is not provided:
112076          *  - The previousValue argument is the value of the first element present in the array.
112077          *  - The currentValue argument is the value of the second element present in the array.
112078          *
112079          * @callback geomReduceCallback
112080          * @param {*} previousValue The accumulated value previously returned in the last invocation
112081          * of the callback, or initialValue, if supplied.
112082          * @param {Geometry} currentGeometry The current Geometry being processed.
112083          * @param {number} featureIndex The current index of the Feature being processed.
112084          * @param {Object} featureProperties The current Feature Properties being processed.
112085          * @param {Array<number>} featureBBox The current Feature BBox being processed.
112086          * @param {number|string} featureId The current Feature Id being processed.
112087          */
112088
112089         /**
112090          * Reduce geometry in any GeoJSON object, similar to Array.reduce().
112091          *
112092          * @name geomReduce
112093          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
112094          * @param {Function} callback a method that takes (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId)
112095          * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
112096          * @returns {*} The value that results from the reduction.
112097          * @example
112098          * var features = turf.featureCollection([
112099          *     turf.point([26, 37], {foo: 'bar'}),
112100          *     turf.point([36, 53], {hello: 'world'})
112101          * ]);
112102          *
112103          * turf.geomReduce(features, function (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
112104          *   //=previousValue
112105          *   //=currentGeometry
112106          *   //=featureIndex
112107          *   //=featureProperties
112108          *   //=featureBBox
112109          *   //=featureId
112110          *   return currentGeometry
112111          * });
112112          */
112113         function geomReduce(geojson, callback, initialValue) {
112114             var previousValue = initialValue;
112115             geomEach(geojson, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
112116                 if (featureIndex === 0 && initialValue === undefined) previousValue = currentGeometry;
112117                 else previousValue = callback(previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId);
112118             });
112119             return previousValue;
112120         }
112121
112122         /**
112123          * Callback for flattenEach
112124          *
112125          * @callback flattenEachCallback
112126          * @param {Feature} currentFeature The current flattened feature being processed.
112127          * @param {number} featureIndex The current index of the Feature being processed.
112128          * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
112129          */
112130
112131         /**
112132          * Iterate over flattened features in any GeoJSON object, similar to
112133          * Array.forEach.
112134          *
112135          * @name flattenEach
112136          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
112137          * @param {Function} callback a method that takes (currentFeature, featureIndex, multiFeatureIndex)
112138          * @example
112139          * var features = turf.featureCollection([
112140          *     turf.point([26, 37], {foo: 'bar'}),
112141          *     turf.multiPoint([[40, 30], [36, 53]], {hello: 'world'})
112142          * ]);
112143          *
112144          * turf.flattenEach(features, function (currentFeature, featureIndex, multiFeatureIndex) {
112145          *   //=currentFeature
112146          *   //=featureIndex
112147          *   //=multiFeatureIndex
112148          * });
112149          */
112150         function flattenEach(geojson, callback) {
112151             geomEach(geojson, function (geometry, featureIndex, properties, bbox, id) {
112152                 // Callback for single geometry
112153                 var type = (geometry === null) ? null : geometry.type;
112154                 switch (type) {
112155                 case null:
112156                 case 'Point':
112157                 case 'LineString':
112158                 case 'Polygon':
112159                     if (callback(feature$1(geometry, properties, {bbox: bbox, id: id}), featureIndex, 0) === false) return false;
112160                     return;
112161                 }
112162
112163                 var geomType;
112164
112165                 // Callback for multi-geometry
112166                 switch (type) {
112167                 case 'MultiPoint':
112168                     geomType = 'Point';
112169                     break;
112170                 case 'MultiLineString':
112171                     geomType = 'LineString';
112172                     break;
112173                 case 'MultiPolygon':
112174                     geomType = 'Polygon';
112175                     break;
112176                 }
112177
112178                 for (var multiFeatureIndex = 0; multiFeatureIndex < geometry.coordinates.length; multiFeatureIndex++) {
112179                     var coordinate = geometry.coordinates[multiFeatureIndex];
112180                     var geom = {
112181                         type: geomType,
112182                         coordinates: coordinate
112183                     };
112184                     if (callback(feature$1(geom, properties), featureIndex, multiFeatureIndex) === false) return false;
112185                 }
112186             });
112187         }
112188
112189         /**
112190          * Takes one or more features and returns their area in square meters.
112191          *
112192          * @name area
112193          * @param {GeoJSON} geojson input GeoJSON feature(s)
112194          * @returns {number} area in square meters
112195          * @example
112196          * var polygon = turf.polygon([[[125, -15], [113, -22], [154, -27], [144, -15], [125, -15]]]);
112197          *
112198          * var area = turf.area(polygon);
112199          *
112200          * //addToMap
112201          * var addToMap = [polygon]
112202          * polygon.properties.area = area
112203          */
112204         function area(geojson) {
112205             return geomReduce(geojson, function (value, geom) {
112206                 return value + calculateArea(geom);
112207             }, 0);
112208         }
112209
112210         var RADIUS$1 = 6378137;
112211         // var FLATTENING_DENOM = 298.257223563;
112212         // var FLATTENING = 1 / FLATTENING_DENOM;
112213         // var POLAR_RADIUS = RADIUS * (1 - FLATTENING);
112214
112215         /**
112216          * Calculate Area
112217          *
112218          * @private
112219          * @param {GeoJSON} geojson GeoJSON
112220          * @returns {number} area
112221          */
112222         function calculateArea(geojson) {
112223             var area = 0, i;
112224             switch (geojson.type) {
112225             case 'Polygon':
112226                 return polygonArea$1(geojson.coordinates);
112227             case 'MultiPolygon':
112228                 for (i = 0; i < geojson.coordinates.length; i++) {
112229                     area += polygonArea$1(geojson.coordinates[i]);
112230                 }
112231                 return area;
112232             case 'Point':
112233             case 'MultiPoint':
112234             case 'LineString':
112235             case 'MultiLineString':
112236                 return 0;
112237             case 'GeometryCollection':
112238                 for (i = 0; i < geojson.geometries.length; i++) {
112239                     area += calculateArea(geojson.geometries[i]);
112240                 }
112241                 return area;
112242             }
112243         }
112244
112245         function polygonArea$1(coords) {
112246             var area = 0;
112247             if (coords && coords.length > 0) {
112248                 area += Math.abs(ringArea$1(coords[0]));
112249                 for (var i = 1; i < coords.length; i++) {
112250                     area -= Math.abs(ringArea$1(coords[i]));
112251                 }
112252             }
112253             return area;
112254         }
112255
112256         /**
112257          * @private
112258          * Calculate the approximate area of the polygon were it projected onto the earth.
112259          * Note that this area will be positive if ring is oriented clockwise, otherwise it will be negative.
112260          *
112261          * Reference:
112262          * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
112263          * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
112264          *
112265          * @param {Array<Array<number>>} coords Ring Coordinates
112266          * @returns {number} The approximate signed geodesic area of the polygon in square meters.
112267          */
112268         function ringArea$1(coords) {
112269             var p1;
112270             var p2;
112271             var p3;
112272             var lowerIndex;
112273             var middleIndex;
112274             var upperIndex;
112275             var i;
112276             var area = 0;
112277             var coordsLength = coords.length;
112278
112279             if (coordsLength > 2) {
112280                 for (i = 0; i < coordsLength; i++) {
112281                     if (i === coordsLength - 2) { // i = N-2
112282                         lowerIndex = coordsLength - 2;
112283                         middleIndex = coordsLength - 1;
112284                         upperIndex = 0;
112285                     } else if (i === coordsLength - 1) { // i = N-1
112286                         lowerIndex = coordsLength - 1;
112287                         middleIndex = 0;
112288                         upperIndex = 1;
112289                     } else { // i = 0 to N-3
112290                         lowerIndex = i;
112291                         middleIndex = i + 1;
112292                         upperIndex = i + 2;
112293                     }
112294                     p1 = coords[lowerIndex];
112295                     p2 = coords[middleIndex];
112296                     p3 = coords[upperIndex];
112297                     area += (rad$1(p3[0]) - rad$1(p1[0])) * Math.sin(rad$1(p2[1]));
112298                 }
112299
112300                 area = area * RADIUS$1 * RADIUS$1 / 2;
112301             }
112302
112303             return area;
112304         }
112305
112306         function rad$1(_) {
112307             return _ * Math.PI / 180;
112308         }
112309
112310         /**
112311          * Get Geometry from Feature or Geometry Object
112312          *
112313          * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
112314          * @returns {Geometry|null} GeoJSON Geometry Object
112315          * @throws {Error} if geojson is not a Feature or Geometry Object
112316          * @example
112317          * var point = {
112318          *   "type": "Feature",
112319          *   "properties": {},
112320          *   "geometry": {
112321          *     "type": "Point",
112322          *     "coordinates": [110, 40]
112323          *   }
112324          * }
112325          * var geom = turf.getGeom(point)
112326          * //={"type": "Point", "coordinates": [110, 40]}
112327          */
112328         function getGeom(geojson) {
112329             if (!geojson) throw new Error('geojson is required');
112330             if (geojson.geometry !== undefined) return geojson.geometry;
112331             if (geojson.coordinates || geojson.geometries) return geojson;
112332             throw new Error('geojson must be a valid Feature or Geometry Object');
112333         }
112334
112335         /**
112336          * Finds the difference between two {@link Polygon|polygons} by clipping the second polygon from the first.
112337          *
112338          * @name difference
112339          * @param {Feature<Polygon|MultiPolygon>} polygon1 input Polygon feature
112340          * @param {Feature<Polygon|MultiPolygon>} polygon2 Polygon feature to difference from polygon1
112341          * @returns {Feature<Polygon|MultiPolygon>|null} a Polygon or MultiPolygon feature showing the area of `polygon1` excluding the area of `polygon2` (if empty returns `null`)
112342          * @example
112343          * var polygon1 = turf.polygon([[
112344          *   [128, -26],
112345          *   [141, -26],
112346          *   [141, -21],
112347          *   [128, -21],
112348          *   [128, -26]
112349          * ]], {
112350          *   "fill": "#F00",
112351          *   "fill-opacity": 0.1
112352          * });
112353          * var polygon2 = turf.polygon([[
112354          *   [126, -28],
112355          *   [140, -28],
112356          *   [140, -20],
112357          *   [126, -20],
112358          *   [126, -28]
112359          * ]], {
112360          *   "fill": "#00F",
112361          *   "fill-opacity": 0.1
112362          * });
112363          *
112364          * var difference = turf.difference(polygon1, polygon2);
112365          *
112366          * //addToMap
112367          * var addToMap = [polygon1, polygon2, difference];
112368          */
112369         function difference(polygon1, polygon2) {
112370             var geom1 = getGeom(polygon1);
112371             var geom2 = getGeom(polygon2);
112372             var properties = polygon1.properties || {};
112373
112374             // Issue #721 - JSTS can't handle empty polygons
112375             geom1 = removeEmptyPolygon(geom1);
112376             geom2 = removeEmptyPolygon(geom2);
112377             if (!geom1) return null;
112378             if (!geom2) return feature$1(geom1, properties);
112379
112380             // JSTS difference operation
112381             var reader = new GeoJSONReader();
112382             var a = reader.read(geom1);
112383             var b = reader.read(geom2);
112384             var differenced = OverlayOp.difference(a, b);
112385             if (differenced.isEmpty()) return null;
112386             var writer = new GeoJSONWriter();
112387             var geom = writer.write(differenced);
112388
112389             return feature$1(geom, properties);
112390         }
112391
112392         /**
112393          * Detect Empty Polygon
112394          *
112395          * @private
112396          * @param {Geometry<Polygon|MultiPolygon>} geom Geometry Object
112397          * @returns {Geometry<Polygon|MultiPolygon>|null} removed any polygons with no areas
112398          */
112399         function removeEmptyPolygon(geom) {
112400             switch (geom.type) {
112401             case 'Polygon':
112402                 if (area(geom) > 1) return geom;
112403                 return null;
112404             case 'MultiPolygon':
112405                 var coordinates = [];
112406                 flattenEach(geom, function (feature$$1) {
112407                     if (area(feature$$1) > 1) coordinates.push(feature$$1.geometry.coordinates);
112408                 });
112409                 if (coordinates.length) return {type: 'MultiPolygon', coordinates: coordinates};
112410             }
112411         }
112412
112413         /**
112414          * 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.
112415          *
112416          * @name union
112417          * @param {...Feature<Polygon>} A polygon to combine
112418          * @returns {Feature<(Polygon|MultiPolygon)>} a combined {@link Polygon} or {@link MultiPolygon} feature
112419          * @example
112420          * var poly1 = turf.polygon([[
112421          *     [-82.574787, 35.594087],
112422          *     [-82.574787, 35.615581],
112423          *     [-82.545261, 35.615581],
112424          *     [-82.545261, 35.594087],
112425          *     [-82.574787, 35.594087]
112426          * ]], {"fill": "#0f0"});
112427          * var poly2 = turf.polygon([[
112428          *     [-82.560024, 35.585153],
112429          *     [-82.560024, 35.602602],
112430          *     [-82.52964, 35.602602],
112431          *     [-82.52964, 35.585153],
112432          *     [-82.560024, 35.585153]
112433          * ]], {"fill": "#00f"});
112434          *
112435          * var union = turf.union(poly1, poly2);
112436          *
112437          * //addToMap
112438          * var addToMap = [poly1, poly2, union];
112439          */
112440         function union$1() {
112441             var reader = new GeoJSONReader();
112442             var result = reader.read(JSON.stringify(arguments[0].geometry));
112443
112444             for (var i = 1; i < arguments.length; i++) {
112445                 result = UnionOp.union(result, reader.read(JSON.stringify(arguments[i].geometry)));
112446             }
112447
112448             var writer = new GeoJSONWriter();
112449             result = writer.write(result);
112450
112451             return {
112452                 type: 'Feature',
112453                 geometry: result,
112454                 properties: arguments[0].properties
112455             };
112456         }
112457
112458         // Reduce an array of locations into a single GeoJSON feature
112459         function _locationReducer(accumulator, location) {
112460           /* eslint-disable no-console, no-invalid-this */
112461           let result;
112462           try {
112463             let resolved = this.resolveLocation(location);
112464             if (!resolved || !resolved.feature) {
112465               console.warn(`Warning:  Couldn't resolve location "${location}"`);
112466               return accumulator;
112467             }
112468             result = !accumulator ? resolved.feature : union$1(accumulator, resolved.feature);
112469           } catch (e) {
112470             console.warn(`Warning:  Error resolving location "${location}"`);
112471             console.warn(e);
112472             result = accumulator;
112473           }
112474
112475           return result;
112476           /* eslint-enable no-console, no-invalid-this */
112477         }
112478
112479
112480
112481         function _cloneDeep(obj) {
112482           return JSON.parse(JSON.stringify(obj));
112483         }
112484
112485
112486         class LocationConflation {
112487
112488           // constructor
112489           //
112490           // Optionally pass a GeoJSON FeatureCollection of known features which we can refer to later.
112491           // Each feature must have a filename-like `id`, for example: `something.geojson`
112492           //
112493           // {
112494           //   "type": "FeatureCollection"
112495           //   "features": [
112496           //     {
112497           //       "type": "Feature",
112498           //       "id": "philly_metro.geojson",
112499           //       "properties": { … },
112500           //       "geometry": { … }
112501           //     }
112502           //   ]
112503           // }
112504           constructor(fc) {
112505             this._cache = {};
112506
112507             // process input FeatureCollection
112508             if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) {
112509               fc.features.forEach(feature => {
112510                 feature.properties = feature.properties || {};
112511                 let props = feature.properties;
112512
112513                 // get `id` from either `id` or `properties`
112514                 let id = feature.id || props.id;
112515                 if (!id || !/^\S+\.geojson$/i.test(id)) return;
112516
112517                 // ensure id exists and is lowercase
112518                 id = id.toLowerCase();
112519                 feature.id = id;
112520                 props.id = id;
112521
112522                 // ensure area property exists
112523                 if (!props.area) {
112524                   const area = geojsonArea.geometry(feature.geometry) / 1e6;  // m² to km²
112525                   props.area = Number(area.toFixed(2));
112526                 }
112527
112528                 this._cache[id] = feature;
112529               });
112530             }
112531
112532             // Replace CountryCoder world geometry to have a polygon covering the world.
112533             let world = _cloneDeep(feature('Q2'));
112534             world.geometry = {
112535               type: 'Polygon',
112536               coordinates: [[[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]]
112537             };
112538             world.id = 'Q2';
112539             world.properties.id = 'Q2';
112540             world.properties.area = geojsonArea.geometry(world.geometry) / 1e6;  // m² to km²
112541             this._cache.Q2 = world;
112542           }
112543
112544
112545           // validateLocation
112546           //
112547           // Pass a `location` identifier
112548           // Returns a result like
112549           //   {
112550           //     type:     'point', 'geojson', or 'countrycoder'
112551           //     location:  the queried location
112552           //     id:        a unique identifier
112553           //   }
112554           //  or `null` if the location is invalid
112555           //
112556           validateLocation(location) {
112557             if (Array.isArray(location)) {   // a [lon,lat] coordinate pair?
112558               if (location.length === 2 && Number.isFinite(location[0]) && Number.isFinite(location[1]) &&
112559                 location[0] >= -180 && location[0] <= 180 && location[1] >= -90 && location[1] <= 90
112560               ) {
112561                 const id = '[' + location.toString() + ']';
112562                 return { type: 'point', location: location, id: id };
112563               }
112564
112565             } else if (typeof location === 'string' && /^\S+\.geojson$/i.test(location)) {   // a .geojson filename?
112566               const id = location.toLowerCase();
112567               if (this._cache[id]) {
112568                 return { type: 'geojson', location: location, id: id };
112569               }
112570
112571             } else if (typeof location === 'string' || typeof location === 'number') {   // a country-coder value?
112572               const feature$1 = feature(location);
112573               if (feature$1) {
112574                 // Use wikidata QID as the identifier, since that seems to be the only
112575                 // property that everything in CountryCoder is guaranteed to have.
112576                 const id = feature$1.properties.wikidata;
112577                 return { type: 'countrycoder', location: location, id: id };
112578               }
112579             }
112580
112581             return null;
112582           }
112583
112584
112585           // resolveLocation
112586           //
112587           // Pass a `location` identifier
112588           // Returns a result like
112589           //   {
112590           //     type:      'point', 'geojson', or 'countrycoder'
112591           //     location:  the queried location
112592           //     id:        a unique identifier
112593           //     feature:   the geojson feature
112594           //   }
112595           //  or `null` if the location is invalid
112596           //
112597           resolveLocation(location) {
112598             const valid = this.validateLocation(location);
112599             if (!valid) return null;
112600
112601             // return a result from cache if we can
112602             if (this._cache[valid.id]) {
112603               return Object.assign(valid, { feature: this._cache[valid.id] });
112604             }
112605
112606             // a [lon,lat] coordinate pair?
112607             if (valid.type === 'point') {
112608               const RADIUS = 25000;  // meters
112609               const EDGES = 10;
112610               const PRECISION = 3;
112611               const area = Math.PI * RADIUS * RADIUS / 1e6;     // m² to km²
112612               const feature = this._cache[valid.id] = geojsonPrecision({
112613                 type: 'Feature',
112614                 id: valid.id,
112615                 properties: { id: valid.id, area: Number(area.toFixed(2)) },
112616                 geometry: circleToPolygon(location, RADIUS, EDGES)
112617               }, PRECISION);
112618               return Object.assign(valid, { feature: feature });
112619
112620             // a .geojson filename?
112621             } else if (valid.type === 'geojson') ; else if (valid.type === 'countrycoder') {
112622               let feature$1 = _cloneDeep(feature(valid.id));
112623               let props = feature$1.properties;
112624
112625               // -> This block of code is weird and requires some explanation. <-
112626               // CountryCoder includes higher level features which are made up of members.
112627               // These features don't have their own geometry, but CountryCoder provides an
112628               //   `aggregateFeature` method to combine these members into a MultiPolygon.
112629               // BUT, when we try to actually work with these aggregated MultiPolygons,
112630               //   Turf/JSTS gets crashy because of topography bugs.
112631               // SO, we'll aggregate the features ourselves by unioning them together.
112632               // This approach also has the benefit of removing all the internal boaders and
112633               //   simplifying the regional polygons a lot.
112634               if (Array.isArray(props.members)) {
112635                 let seed = feature$1.geometry ? feature$1 : null;
112636                 let aggregate = props.members.reduce(_locationReducer.bind(this), seed);
112637                 feature$1.geometry = aggregate.geometry;
112638               }
112639
112640               // ensure area property exists
112641               if (!props.area) {
112642                 const area = geojsonArea.geometry(feature$1.geometry) / 1e6;  // m² to km²
112643                 props.area = Number(area.toFixed(2));
112644               }
112645
112646               // ensure id property exists
112647               feature$1.id = valid.id;
112648               props.id = valid.id;
112649
112650               this._cache[valid.id] = feature$1;
112651               return Object.assign(valid, { feature: feature$1 });
112652             }
112653
112654             return null;
112655           }
112656
112657
112658           // resolveLocationSet
112659           //
112660           // Pass a `locationSet` Object like:
112661           //   `{ include: [ Array ], exclude: [ Array ] }`
112662           // Returns a stable identifier string of the form:
112663           //   "+[included]-[excluded]"
112664           //
112665           resolveLocationSet(locationSet) {
112666             locationSet = locationSet || {};
112667             const resolve = this.resolveLocation.bind(this);
112668             let include = (locationSet.include || []).map(resolve).filter(Boolean);
112669             let exclude = (locationSet.exclude || []).map(resolve).filter(Boolean);
112670
112671             if (!include.length) {
112672               include = [resolve('Q2')];   // default to 'the world'
112673             }
112674
112675             // return quickly if it's a single included location..
112676             if (include.length === 1 && exclude.length === 0) {
112677               return include[0].feature;
112678             }
112679
112680             // generate stable identifier
112681             include.sort(sortFeatures);
112682             let id = '+[' + include.map(d => d.id).join(',') + ']';
112683             if (exclude.length) {
112684               exclude.sort(sortFeatures);
112685               id += '-[' + exclude.map(d => d.id).join(',') + ']';
112686             }
112687
112688             // return cached?
112689             if (this._cache[id]) {
112690               return this._cache[id];
112691             }
112692
112693             // calculate unions
112694             let includeGeoJSON = include.map(d => d.location).reduce(_locationReducer.bind(this), null);
112695             let excludeGeoJSON = exclude.map(d => d.location).reduce(_locationReducer.bind(this), null);
112696
112697             // calculate difference, update area and return result
112698             let resultGeoJSON = excludeGeoJSON ? difference(includeGeoJSON, excludeGeoJSON) : includeGeoJSON;
112699             const area = geojsonArea.geometry(resultGeoJSON.geometry) / 1e6;  // m² to km²
112700             resultGeoJSON.id = id;
112701             resultGeoJSON.properties = { id: id, area: Number(area.toFixed(2)) };
112702
112703             return this._cache[id] = resultGeoJSON;
112704
112705
112706             // Sorting the location lists is ok because they end up unioned together.
112707             // This sorting makes it possible to generate a deterministic id.
112708             function sortFeatures(a, b) {
112709               const rank = { countrycoder: 1, geojson: 2, point: 3 };
112710               const aRank = rank[a.type];
112711               const bRank = rank[b.type];
112712
112713               return (aRank > bRank) ? 1
112714                 : (aRank < bRank) ? -1
112715                 : a.id.localeCompare(b.id);
112716             }
112717           }
112718
112719
112720           cache() {
112721             return this._cache;
112722           }
112723         }
112724
112725         let _oci = null;
112726
112727         function uiSuccess(context) {
112728           const MAXEVENTS = 2;
112729           const dispatch$1 = dispatch('cancel');
112730           let _changeset;
112731           let _location;
112732           ensureOSMCommunityIndex();   // start fetching the data
112733
112734
112735           function ensureOSMCommunityIndex() {
112736             const data = _mainFileFetcher;
112737             return Promise.all([ data.get('oci_resources'), data.get('oci_features') ])
112738               .then(vals => {
112739                 if (_oci) return _oci;
112740
112741                 const ociResources = vals[0].resources;
112742                 const loco = new LocationConflation(vals[1]);
112743                 let ociFeatures = {};
112744
112745                 Object.values(ociResources).forEach(resource => {
112746                   const feature = loco.resolveLocationSet(resource.locationSet);
112747                   let ociFeature = ociFeatures[feature.id];
112748                   if (!ociFeature) {
112749                     ociFeature = JSON.parse(JSON.stringify(feature));  // deep clone
112750                     ociFeature.properties.resourceIDs = new Set();
112751                     ociFeatures[feature.id] = ociFeature;
112752                   }
112753                   ociFeature.properties.resourceIDs.add(resource.id);
112754                 });
112755
112756                 return _oci = {
112757                   features: ociFeatures,
112758                   resources: ociResources,
112759                   query: whichPolygon_1({ type: 'FeatureCollection', features: Object.values(ociFeatures) })
112760                 };
112761               });
112762           }
112763
112764
112765           // string-to-date parsing in JavaScript is weird
112766           function parseEventDate(when) {
112767             if (!when) return;
112768
112769             let raw = when.trim();
112770             if (!raw) return;
112771
112772             if (!/Z$/.test(raw)) {   // if no trailing 'Z', add one
112773               raw += 'Z';            // this forces date to be parsed as a UTC date
112774             }
112775
112776             const parsed = new Date(raw);
112777             return new Date(parsed.toUTCString().substr(0, 25));  // convert to local timezone
112778           }
112779
112780
112781           function success(selection) {
112782             let header = selection
112783               .append('div')
112784               .attr('class', 'header fillL');
112785
112786             header
112787               .append('h3')
112788               .text(_t('success.just_edited'));
112789
112790             header
112791               .append('button')
112792               .attr('class', 'close')
112793               .on('click', () => dispatch$1.call('cancel'))
112794               .call(svgIcon('#iD-icon-close'));
112795
112796             let body = selection
112797               .append('div')
112798               .attr('class', 'body save-success fillL');
112799
112800             let summary = body
112801               .append('div')
112802               .attr('class', 'save-summary');
112803
112804             summary
112805               .append('h3')
112806               .text(_t('success.thank_you' + (_location ? '_location' : ''), { where: _location }));
112807
112808             summary
112809               .append('p')
112810               .text(_t('success.help_html'))
112811               .append('a')
112812               .attr('class', 'link-out')
112813               .attr('target', '_blank')
112814               .attr('tabindex', -1)
112815               .attr('href', _t('success.help_link_url'))
112816               .call(svgIcon('#iD-icon-out-link', 'inline'))
112817               .append('span')
112818               .text(_t('success.help_link_text'));
112819
112820             let osm = context.connection();
112821             if (!osm) return;
112822
112823             let changesetURL = osm.changesetURL(_changeset.id);
112824
112825             let table = summary
112826               .append('table')
112827               .attr('class', 'summary-table');
112828
112829             let row = table
112830               .append('tr')
112831               .attr('class', 'summary-row');
112832
112833             row
112834               .append('td')
112835               .attr('class', 'cell-icon summary-icon')
112836               .append('a')
112837               .attr('target', '_blank')
112838               .attr('href', changesetURL)
112839               .append('svg')
112840               .attr('class', 'logo-small')
112841               .append('use')
112842               .attr('xlink:href', '#iD-logo-osm');
112843
112844             let summaryDetail = row
112845               .append('td')
112846               .attr('class', 'cell-detail summary-detail');
112847
112848             summaryDetail
112849               .append('a')
112850               .attr('class', 'cell-detail summary-view-on-osm')
112851               .attr('target', '_blank')
112852               .attr('href', changesetURL)
112853               .text(_t('success.view_on_osm'));
112854
112855             summaryDetail
112856               .append('div')
112857               .html(_t('success.changeset_id', {
112858                 changeset_id: `<a href="${changesetURL}" target="_blank">${_changeset.id}</a>`
112859               }));
112860
112861
112862             // Get OSM community index features intersecting the map..
112863             ensureOSMCommunityIndex()
112864               .then(oci => {
112865                 let communities = [];
112866                 const properties = oci.query(context.map().center(), true) || [];
112867
112868                 // Gather the communities from the result
112869                 properties.forEach(props => {
112870                   const resourceIDs = Array.from(props.resourceIDs);
112871                   resourceIDs.forEach(resourceID => {
112872                     const resource = oci.resources[resourceID];
112873                     communities.push({
112874                       area: props.area || Infinity,
112875                       order: resource.order || 0,
112876                       resource: resource
112877                     });
112878                   });
112879                 });
112880
112881                 // sort communities by feature area ascending, community order descending
112882                 communities.sort((a, b) => a.area - b.area || b.order - a.order);
112883
112884                 body
112885                   .call(showCommunityLinks, communities.map(c => c.resource));
112886               });
112887           }
112888
112889
112890           function showCommunityLinks(selection, resources) {
112891             let communityLinks = selection
112892               .append('div')
112893               .attr('class', 'save-communityLinks');
112894
112895             communityLinks
112896               .append('h3')
112897               .text(_t('success.like_osm'));
112898
112899             let table = communityLinks
112900               .append('table')
112901               .attr('class', 'community-table');
112902
112903             let row = table.selectAll('.community-row')
112904               .data(resources);
112905
112906             let rowEnter = row.enter()
112907               .append('tr')
112908               .attr('class', 'community-row');
112909
112910             rowEnter
112911               .append('td')
112912               .attr('class', 'cell-icon community-icon')
112913               .append('a')
112914               .attr('target', '_blank')
112915               .attr('href', d => d.url)
112916               .append('svg')
112917               .attr('class', 'logo-small')
112918               .append('use')
112919               .attr('xlink:href', d => `#community-${d.type}`);
112920
112921             let communityDetail = rowEnter
112922               .append('td')
112923               .attr('class', 'cell-detail community-detail');
112924
112925             communityDetail
112926               .each(showCommunityDetails);
112927
112928             communityLinks
112929               .append('div')
112930               .attr('class', 'community-missing')
112931               .text(_t('success.missing'))
112932               .append('a')
112933               .attr('class', 'link-out')
112934               .attr('target', '_blank')
112935               .attr('tabindex', -1)
112936               .call(svgIcon('#iD-icon-out-link', 'inline'))
112937               .attr('href', 'https://github.com/osmlab/osm-community-index/issues')
112938               .append('span')
112939               .text(_t('success.tell_us'));
112940           }
112941
112942
112943           function showCommunityDetails(d) {
112944             let selection = select(this);
112945             let communityID = d.id;
112946             let replacements = {
112947               url: linkify(d.url),
112948               signupUrl: linkify(d.signupUrl || d.url)
112949             };
112950
112951             selection
112952               .append('div')
112953               .attr('class', 'community-name')
112954               .append('a')
112955               .attr('target', '_blank')
112956               .attr('href', d.url)
112957               .text(_t(`community.${d.id}.name`));
112958
112959             let descriptionHTML = _t(`community.${d.id}.description`, replacements);
112960
112961             if (d.type === 'reddit') {   // linkify subreddits  #4997
112962               descriptionHTML = descriptionHTML
112963                 .replace(/(\/r\/\w*\/*)/i, match => linkify(d.url, match));
112964             }
112965
112966             selection
112967               .append('div')
112968               .attr('class', 'community-description')
112969               .html(descriptionHTML);
112970
112971             if (d.extendedDescription || (d.languageCodes && d.languageCodes.length)) {
112972               selection
112973                 .append('div')
112974                 .call(uiDisclosure(context, `community-more-${d.id}`, false)
112975                   .expanded(false)
112976                   .updatePreference(false)
112977                   .title(_t('success.more'))
112978                   .content(showMore)
112979                 );
112980             }
112981
112982             let nextEvents = (d.events || [])
112983               .map(event => {
112984                 event.date = parseEventDate(event.when);
112985                 return event;
112986               })
112987               .filter(event => {      // date is valid and future (or today)
112988                 const t = event.date.getTime();
112989                 const now = (new Date()).setHours(0,0,0,0);
112990                 return !isNaN(t) && t >= now;
112991               })
112992               .sort((a, b) => {       // sort by date ascending
112993                 return a.date < b.date ? -1 : a.date > b.date ? 1 : 0;
112994               })
112995               .slice(0, MAXEVENTS);   // limit number of events shown
112996
112997             if (nextEvents.length) {
112998               selection
112999                 .append('div')
113000                 .call(uiDisclosure(context, `community-events-${d.id}`, false)
113001                   .expanded(false)
113002                   .updatePreference(false)
113003                   .title(_t('success.events'))
113004                   .content(showNextEvents)
113005                 )
113006                 .select('.hide-toggle')
113007                 .append('span')
113008                 .attr('class', 'badge-text')
113009                 .text(nextEvents.length);
113010             }
113011
113012
113013             function showMore(selection) {
113014               let more = selection.selectAll('.community-more')
113015                 .data([0]);
113016
113017               let moreEnter = more.enter()
113018                 .append('div')
113019                 .attr('class', 'community-more');
113020
113021               if (d.extendedDescription) {
113022                 moreEnter
113023                   .append('div')
113024                   .attr('class', 'community-extended-description')
113025                   .html(_t(`community.${d.id}.extendedDescription`, replacements));
113026               }
113027
113028               if (d.languageCodes && d.languageCodes.length) {
113029                 const languageList = d.languageCodes
113030                   .map(code => _mainLocalizer.languageName(code))
113031                   .join(', ');
113032
113033                 moreEnter
113034                   .append('div')
113035                   .attr('class', 'community-languages')
113036                   .text(_t('success.languages', { languages: languageList }));
113037               }
113038             }
113039
113040
113041             function showNextEvents(selection) {
113042               let events = selection
113043                 .append('div')
113044                 .attr('class', 'community-events');
113045
113046               let item = events.selectAll('.community-event')
113047                 .data(nextEvents);
113048
113049               let itemEnter = item.enter()
113050                 .append('div')
113051                 .attr('class', 'community-event');
113052
113053               itemEnter
113054                 .append('div')
113055                 .attr('class', 'community-event-name')
113056                 .append('a')
113057                 .attr('target', '_blank')
113058                 .attr('href', d => d.url)
113059                 .text(d => {
113060                   let name = d.name;
113061                   if (d.i18n && d.id) {
113062                     name = _t(`community.${communityID}.events.${d.id}.name`, { default: name });
113063                   }
113064                   return name;
113065                 });
113066
113067               itemEnter
113068                 .append('div')
113069                 .attr('class', 'community-event-when')
113070                 .text(d => {
113071                   let options = { weekday: 'short', day: 'numeric', month: 'short', year: 'numeric' };
113072                   if (d.date.getHours() || d.date.getMinutes()) {   // include time if it has one
113073                     options.hour = 'numeric';
113074                     options.minute = 'numeric';
113075                   }
113076                   return d.date.toLocaleString(_mainLocalizer.localeCode(), options);
113077                 });
113078
113079               itemEnter
113080                 .append('div')
113081                 .attr('class', 'community-event-where')
113082                 .text(d => {
113083                   let where = d.where;
113084                   if (d.i18n && d.id) {
113085                     where = _t(`community.${communityID}.events.${d.id}.where`, { default: where });
113086                   }
113087                   return where;
113088                 });
113089
113090               itemEnter
113091                 .append('div')
113092                 .attr('class', 'community-event-description')
113093                 .text(d => {
113094                   let description = d.description;
113095                   if (d.i18n && d.id) {
113096                     description = _t(`community.${communityID}.events.${d.id}.description`, { default: description });
113097                   }
113098                   return description;
113099                 });
113100             }
113101
113102
113103             function linkify(url, text) {
113104               text = text || url;
113105               return `<a target="_blank" href="${url}">${text}</a>`;
113106             }
113107           }
113108
113109
113110           success.changeset = function(val) {
113111             if (!arguments.length) return _changeset;
113112             _changeset = val;
113113             return success;
113114           };
113115
113116
113117           success.location = function(val) {
113118             if (!arguments.length) return _location;
113119             _location = val;
113120             return success;
113121           };
113122
113123
113124           return utilRebind(success, dispatch$1, 'on');
113125         }
113126
113127         function modeSave(context) {
113128             var mode = { id: 'save' };
113129             var keybinding = utilKeybinding('modeSave');
113130
113131             var commit = uiCommit(context)
113132                 .on('cancel', cancel);
113133             var _conflictsUi; // uiConflicts
113134
113135             var _location;
113136             var _success;
113137
113138             var uploader = context.uploader()
113139                 .on('saveStarted.modeSave', function() {
113140                     keybindingOff();
113141                 })
113142                 // fire off some async work that we want to be ready later
113143                 .on('willAttemptUpload.modeSave', prepareForSuccess)
113144                 .on('progressChanged.modeSave', showProgress)
113145                 .on('resultNoChanges.modeSave', function() {
113146                     cancel();
113147                 })
113148                 .on('resultErrors.modeSave', showErrors)
113149                 .on('resultConflicts.modeSave', showConflicts)
113150                 .on('resultSuccess.modeSave', showSuccess);
113151
113152
113153             function cancel() {
113154                 context.enter(modeBrowse(context));
113155             }
113156
113157
113158             function showProgress(num, total) {
113159                 var modal = context.container().select('.loading-modal .modal-section');
113160                 var progress = modal.selectAll('.progress')
113161                     .data([0]);
113162
113163                 // enter/update
113164                 progress.enter()
113165                     .append('div')
113166                     .attr('class', 'progress')
113167                     .merge(progress)
113168                     .text(_t('save.conflict_progress', { num: num, total: total }));
113169             }
113170
113171
113172             function showConflicts(changeset, conflicts, origChanges) {
113173
113174                 var selection = context.container()
113175                     .select('.sidebar')
113176                     .append('div')
113177                     .attr('class','sidebar-component');
113178
113179                 context.container().selectAll('.main-content')
113180                     .classed('active', true)
113181                     .classed('inactive', false);
113182
113183                 _conflictsUi = uiConflicts(context)
113184                     .conflictList(conflicts)
113185                     .origChanges(origChanges)
113186                     .on('cancel', function() {
113187                         context.container().selectAll('.main-content')
113188                             .classed('active', false)
113189                             .classed('inactive', true);
113190                         selection.remove();
113191                         keybindingOn();
113192
113193                         uploader.cancelConflictResolution();
113194                     })
113195                     .on('save', function() {
113196                         context.container().selectAll('.main-content')
113197                             .classed('active', false)
113198                             .classed('inactive', true);
113199                         selection.remove();
113200
113201                         uploader.processResolvedConflicts(changeset);
113202                     });
113203
113204                 selection.call(_conflictsUi);
113205             }
113206
113207
113208             function showErrors(errors) {
113209                 keybindingOn();
113210
113211                 var selection = uiConfirm(context.container());
113212                 selection
113213                     .select('.modal-section.header')
113214                     .append('h3')
113215                     .text(_t('save.error'));
113216
113217                 addErrors(selection, errors);
113218                 selection.okButton();
113219             }
113220
113221
113222             function addErrors(selection, data) {
113223                 var message = selection
113224                     .select('.modal-section.message-text');
113225
113226                 var items = message
113227                     .selectAll('.error-container')
113228                     .data(data);
113229
113230                 var enter = items.enter()
113231                     .append('div')
113232                     .attr('class', 'error-container');
113233
113234                 enter
113235                     .append('a')
113236                     .attr('class', 'error-description')
113237                     .attr('href', '#')
113238                     .classed('hide-toggle', true)
113239                     .text(function(d) { return d.msg || _t('save.unknown_error_details'); })
113240                     .on('click', function() {
113241                         event.preventDefault();
113242
113243                         var error = select(this);
113244                         var detail = select(this.nextElementSibling);
113245                         var exp = error.classed('expanded');
113246
113247                         detail.style('display', exp ? 'none' : 'block');
113248                         error.classed('expanded', !exp);
113249                     });
113250
113251                 var details = enter
113252                     .append('div')
113253                     .attr('class', 'error-detail-container')
113254                     .style('display', 'none');
113255
113256                 details
113257                     .append('ul')
113258                     .attr('class', 'error-detail-list')
113259                     .selectAll('li')
113260                     .data(function(d) { return d.details || []; })
113261                     .enter()
113262                     .append('li')
113263                     .attr('class', 'error-detail-item')
113264                     .text(function(d) { return d; });
113265
113266                 items.exit()
113267                     .remove();
113268             }
113269
113270
113271             function showSuccess(changeset) {
113272                 commit.reset();
113273
113274                 var ui = _success
113275                     .changeset(changeset)
113276                     .location(_location)
113277                     .on('cancel', function() { context.ui().sidebar.hide(); });
113278
113279                 context.enter(modeBrowse(context).sidebar(ui));
113280             }
113281
113282
113283             function keybindingOn() {
113284                 select(document)
113285                     .call(keybinding.on('⎋', cancel, true));
113286             }
113287
113288
113289             function keybindingOff() {
113290                 select(document)
113291                     .call(keybinding.unbind);
113292             }
113293
113294
113295             // Reverse geocode current map location so we can display a message on
113296             // the success screen like "Thank you for editing around place, region."
113297             function prepareForSuccess() {
113298                 _success = uiSuccess(context);
113299                 _location = null;
113300                 if (!services.geocoder) return;
113301
113302                 services.geocoder.reverse(context.map().center(), function(err, result) {
113303                     if (err || !result || !result.address) return;
113304
113305                     var addr = result.address;
113306                     var place = (addr && (addr.town || addr.city || addr.county)) || '';
113307                     var region = (addr && (addr.state || addr.country)) || '';
113308                     var separator = (place && region) ? _t('success.thank_you_where.separator') : '';
113309
113310                     _location = _t('success.thank_you_where.format',
113311                         { place: place, separator: separator, region: region }
113312                     );
113313                 });
113314             }
113315
113316
113317             mode.selectedIDs = function() {
113318                 return _conflictsUi ? _conflictsUi.shownEntityIds() : [];
113319             };
113320
113321
113322             mode.enter = function() {
113323                 // Show sidebar
113324                 context.ui().sidebar.expand();
113325
113326                 function done() {
113327                     context.ui().sidebar.show(commit);
113328                 }
113329
113330                 keybindingOn();
113331
113332                 context.container().selectAll('.main-content')
113333                     .classed('active', false)
113334                     .classed('inactive', true);
113335
113336                 var osm = context.connection();
113337                 if (!osm) {
113338                     cancel();
113339                     return;
113340                 }
113341
113342                 if (osm.authenticated()) {
113343                     done();
113344                 } else {
113345                     osm.authenticate(function(err) {
113346                         if (err) {
113347                             cancel();
113348                         } else {
113349                             done();
113350                         }
113351                     });
113352                 }
113353             };
113354
113355
113356             mode.exit = function() {
113357
113358                 keybindingOff();
113359
113360                 context.container().selectAll('.main-content')
113361                     .classed('active', true)
113362                     .classed('inactive', false);
113363
113364                 context.ui().sidebar.hide();
113365             };
113366
113367             return mode;
113368         }
113369
113370         function uiToolOldDrawModes(context) {
113371
113372             var tool = {
113373                 id: 'old_modes',
113374                 label: _t('toolbar.add_feature')
113375             };
113376
113377             var modes = [
113378                 modeAddPoint(context, {
113379                     title: _t('modes.add_point.title'),
113380                     button: 'point',
113381                     description: _t('modes.add_point.description'),
113382                     preset: _mainPresetIndex.item('point'),
113383                     key: '1'
113384                 }),
113385                 modeAddLine(context, {
113386                     title: _t('modes.add_line.title'),
113387                     button: 'line',
113388                     description: _t('modes.add_line.description'),
113389                     preset: _mainPresetIndex.item('line'),
113390                     key: '2'
113391                 }),
113392                 modeAddArea(context, {
113393                     title: _t('modes.add_area.title'),
113394                     button: 'area',
113395                     description: _t('modes.add_area.description'),
113396                     preset: _mainPresetIndex.item('area'),
113397                     key: '3'
113398                 })
113399             ];
113400
113401
113402             function enabled() {
113403                 return osmEditable();
113404             }
113405
113406             function osmEditable() {
113407                 return context.editable();
113408             }
113409
113410             modes.forEach(function(mode) {
113411                 context.keybinding().on(mode.key, function() {
113412                     if (!enabled()) return;
113413
113414                     if (mode.id === context.mode().id) {
113415                         context.enter(modeBrowse(context));
113416                     } else {
113417                         context.enter(mode);
113418                     }
113419                 });
113420             });
113421
113422             tool.render = function(selection) {
113423
113424                 var wrap = selection
113425                     .append('div')
113426                     .attr('class', 'joined')
113427                     .style('display', 'flex');
113428
113429                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113430
113431                 context.map()
113432                     .on('move.modes', debouncedUpdate)
113433                     .on('drawn.modes', debouncedUpdate);
113434
113435                 context
113436                     .on('enter.modes', update);
113437
113438                 update();
113439
113440
113441                 function update() {
113442
113443                     var buttons = wrap.selectAll('button.add-button')
113444                         .data(modes, function(d) { return d.id; });
113445
113446                     // exit
113447                     buttons.exit()
113448                         .remove();
113449
113450                     // enter
113451                     var buttonsEnter = buttons.enter()
113452                         .append('button')
113453                         .attr('class', function(d) { return d.id + ' add-button bar-button'; })
113454                         .on('click.mode-buttons', function(d) {
113455                             if (!enabled()) return;
113456
113457                             // When drawing, ignore accidental clicks on mode buttons - #4042
113458                             var currMode = context.mode().id;
113459                             if (/^draw/.test(currMode)) return;
113460
113461                             if (d.id === currMode) {
113462                                 context.enter(modeBrowse(context));
113463                             } else {
113464                                 context.enter(d);
113465                             }
113466                         })
113467                         .call(uiTooltip()
113468                             .placement('bottom')
113469                             .title(function(d) { return d.description; })
113470                             .keys(function(d) { return [d.key]; })
113471                             .scrollContainer(context.container().select('.top-toolbar'))
113472                         );
113473
113474                     buttonsEnter
113475                         .each(function(d) {
113476                             select(this)
113477                                 .call(svgIcon('#iD-icon-' + d.button));
113478                         });
113479
113480                     buttonsEnter
113481                         .append('span')
113482                         .attr('class', 'label')
113483                         .text(function(mode) { return mode.title; });
113484
113485                     // if we are adding/removing the buttons, check if toolbar has overflowed
113486                     if (buttons.enter().size() || buttons.exit().size()) {
113487                         context.ui().checkOverflow('.top-toolbar', true);
113488                     }
113489
113490                     // update
113491                     buttons = buttons
113492                         .merge(buttonsEnter)
113493                         .classed('disabled', function(d) { return !enabled(); })
113494                         .classed('active', function(d) { return context.mode() && context.mode().button === d.button; });
113495                 }
113496             };
113497
113498             return tool;
113499         }
113500
113501         function uiToolNotes(context) {
113502
113503             var tool = {
113504                 id: 'notes',
113505                 label: _t('modes.add_note.label')
113506             };
113507
113508             var mode = modeAddNote(context);
113509
113510             function enabled() {
113511                 return notesEnabled() && notesEditable();
113512             }
113513
113514             function notesEnabled() {
113515                 var noteLayer = context.layers().layer('notes');
113516                 return noteLayer && noteLayer.enabled();
113517             }
113518
113519             function notesEditable() {
113520                 var mode = context.mode();
113521                 return context.map().notesEditable() && mode && mode.id !== 'save';
113522             }
113523
113524             context.keybinding().on(mode.key, function() {
113525                 if (!enabled()) return;
113526
113527                 if (mode.id === context.mode().id) {
113528                     context.enter(modeBrowse(context));
113529                 } else {
113530                     context.enter(mode);
113531                 }
113532             });
113533
113534             tool.render = function(selection) {
113535
113536
113537                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113538
113539                 context.map()
113540                     .on('move.notes', debouncedUpdate)
113541                     .on('drawn.notes', debouncedUpdate);
113542
113543                 context
113544                     .on('enter.notes', update);
113545
113546                 update();
113547
113548
113549                 function update() {
113550                     var showNotes = notesEnabled();
113551                     var data = showNotes ? [mode] : [];
113552
113553                     var buttons = selection.selectAll('button.add-button')
113554                         .data(data, function(d) { return d.id; });
113555
113556                     // exit
113557                     buttons.exit()
113558                         .remove();
113559
113560                     // enter
113561                     var buttonsEnter = buttons.enter()
113562                         .append('button')
113563                         .attr('tabindex', -1)
113564                         .attr('class', function(d) { return d.id + ' add-button bar-button'; })
113565                         .on('click.notes', function(d) {
113566                             if (!enabled()) return;
113567
113568                             // When drawing, ignore accidental clicks on mode buttons - #4042
113569                             var currMode = context.mode().id;
113570                             if (/^draw/.test(currMode)) return;
113571
113572                             if (d.id === currMode) {
113573                                 context.enter(modeBrowse(context));
113574                             } else {
113575                                 context.enter(d);
113576                             }
113577                         })
113578                         .call(uiTooltip()
113579                             .placement('bottom')
113580                             .title(function(d) { return d.description; })
113581                             .keys(function(d) { return [d.key]; })
113582                             .scrollContainer(context.container().select('.top-toolbar'))
113583                         );
113584
113585                     buttonsEnter
113586                         .each(function(d) {
113587                             select(this)
113588                                 .call(svgIcon(d.icon || '#iD-icon-' + d.button));
113589                         });
113590
113591                     // if we are adding/removing the buttons, check if toolbar has overflowed
113592                     if (buttons.enter().size() || buttons.exit().size()) {
113593                         context.ui().checkOverflow('.top-toolbar', true);
113594                     }
113595
113596                     // update
113597                     buttons = buttons
113598                         .merge(buttonsEnter)
113599                         .classed('disabled', function(d) { return !enabled(); })
113600                         .classed('active', function(d) { return context.mode() && context.mode().button === d.button; });
113601                 }
113602             };
113603
113604             tool.uninstall = function() {
113605                 context
113606                     .on('enter.editor.notes', null)
113607                     .on('exit.editor.notes', null)
113608                     .on('enter.notes', null);
113609
113610                 context.map()
113611                     .on('move.notes', null)
113612                     .on('drawn.notes', null);
113613             };
113614
113615             return tool;
113616         }
113617
113618         function uiToolSave(context) {
113619
113620             var tool = {
113621                 id: 'save',
113622                 label: _t('save.title')
113623             };
113624
113625             var button = null;
113626             var tooltipBehavior = null;
113627             var history = context.history();
113628             var key = uiCmd('⌘S');
113629             var _numChanges = 0;
113630
113631             function isSaving() {
113632                 var mode = context.mode();
113633                 return mode && mode.id === 'save';
113634             }
113635
113636             function isDisabled() {
113637                 return _numChanges === 0 || isSaving();
113638             }
113639
113640             function save() {
113641                 event.preventDefault();
113642                 if (!context.inIntro() && !isSaving() && history.hasChanges()) {
113643                     context.enter(modeSave(context));
113644                 }
113645             }
113646
113647             function bgColor() {
113648                 var step;
113649                 if (_numChanges === 0) {
113650                     return null;
113651                 } else if (_numChanges <= 50) {
113652                     step = _numChanges / 50;
113653                     return d3_interpolateRgb('#fff', '#ff8')(step);  // white -> yellow
113654                 } else {
113655                     step = Math.min((_numChanges - 50) / 50, 1.0);
113656                     return d3_interpolateRgb('#ff8', '#f88')(step);  // yellow -> red
113657                 }
113658             }
113659
113660             function updateCount() {
113661                 var val = history.difference().summary().length;
113662                 if (val === _numChanges) return;
113663
113664                 _numChanges = val;
113665
113666                 if (tooltipBehavior) {
113667                     tooltipBehavior
113668                         .title(_t(_numChanges > 0 ? 'save.help' : 'save.no_changes'))
113669                         .keys([key]);
113670                 }
113671
113672                 if (button) {
113673                     button
113674                         .classed('disabled', isDisabled())
113675                         .style('background', bgColor());
113676
113677                     button.select('span.count')
113678                         .text(_numChanges);
113679                 }
113680             }
113681
113682
113683             tool.render = function(selection) {
113684                 tooltipBehavior = uiTooltip()
113685                     .placement('bottom')
113686                     .title(_t('save.no_changes'))
113687                     .keys([key])
113688                     .scrollContainer(context.container().select('.top-toolbar'));
113689
113690                 var lastPointerUpType;
113691
113692                 button = selection
113693                     .append('button')
113694                     .attr('class', 'save disabled bar-button')
113695                     .on('pointerup', function() {
113696                         lastPointerUpType = event.pointerType;
113697                     })
113698                     .on('click', function() {
113699                         event.preventDefault();
113700
113701                         save();
113702
113703                         if (_numChanges === 0 && (
113704                             lastPointerUpType === 'touch' ||
113705                             lastPointerUpType === 'pen')
113706                         ) {
113707                             // there are no tooltips for touch interactions so flash feedback instead
113708                             context.ui().flash
113709                                 .duration(2000)
113710                                 .iconName('#iD-icon-save')
113711                                 .iconClass('disabled')
113712                                 .text(_t('save.no_changes'))();
113713                         }
113714                         lastPointerUpType = null;
113715                     })
113716                     .call(tooltipBehavior);
113717
113718                 button
113719                     .call(svgIcon('#iD-icon-save'));
113720
113721                 button
113722                     .append('span')
113723                     .attr('class', 'count')
113724                     .attr('aria-hidden', 'true')
113725                     .text('0');
113726
113727                 updateCount();
113728
113729
113730                 context.keybinding()
113731                     .on(key, save, true);
113732
113733
113734                 context.history()
113735                     .on('change.save', updateCount);
113736
113737                 context
113738                     .on('enter.save', function() {
113739                         if (button) {
113740                             button
113741                                 .classed('disabled', isDisabled());
113742
113743                             if (isSaving()) {
113744                                 button.call(tooltipBehavior.hide);
113745                             }
113746                         }
113747                     });
113748             };
113749
113750
113751             tool.uninstall = function() {
113752                 context.keybinding()
113753                     .off(key, true);
113754
113755                 context.history()
113756                     .on('change.save', null);
113757
113758                 context
113759                     .on('enter.save', null);
113760
113761                 button = null;
113762                 tooltipBehavior = null;
113763             };
113764
113765             return tool;
113766         }
113767
113768         function uiToolSidebarToggle(context) {
113769
113770             var tool = {
113771                 id: 'sidebar_toggle',
113772                 label: _t('toolbar.inspect')
113773             };
113774
113775             tool.render = function(selection) {
113776                 selection
113777                     .append('button')
113778                     .attr('class', 'bar-button')
113779                     .on('click', function() {
113780                         context.ui().sidebar.toggle();
113781                     })
113782                     .call(uiTooltip()
113783                         .placement('bottom')
113784                         .title(_t('sidebar.tooltip'))
113785                         .keys([_t('sidebar.key')])
113786                         .scrollContainer(context.container().select('.top-toolbar'))
113787                     )
113788                     .call(svgIcon('#iD-icon-sidebar-' + (_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')));
113789             };
113790
113791             return tool;
113792         }
113793
113794         function uiToolUndoRedo(context) {
113795
113796             var tool = {
113797                 id: 'undo_redo',
113798                 label: _t('toolbar.undo_redo')
113799             };
113800
113801             var commands = [{
113802                 id: 'undo',
113803                 cmd: uiCmd('⌘Z'),
113804                 action: function() {
113805                     context.undo();
113806                 },
113807                 annotation: function() {
113808                     return context.history().undoAnnotation();
113809                 },
113810                 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')
113811             }, {
113812                 id: 'redo',
113813                 cmd: uiCmd('⌘⇧Z'),
113814                 action: function() {
113815                     context.redo();
113816                 },
113817                 annotation: function() {
113818                     return context.history().redoAnnotation();
113819                 },
113820                 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'undo' : 'redo')
113821             }];
113822
113823
113824             function editable() {
113825                 return context.mode() && context.mode().id !== 'save' && context.map().editableDataEnabled(true /* ignore min zoom */);
113826             }
113827
113828
113829             tool.render = function(selection) {
113830                 var tooltipBehavior = uiTooltip()
113831                     .placement('bottom')
113832                     .title(function (d) {
113833                         return d.annotation() ?
113834                             _t(d.id + '.tooltip', { action: d.annotation() }) :
113835                             _t(d.id + '.nothing');
113836                     })
113837                     .keys(function(d) {
113838                         return [d.cmd];
113839                     })
113840                     .scrollContainer(context.container().select('.top-toolbar'));
113841
113842                 var lastPointerUpType;
113843
113844                 var buttons = selection.selectAll('button')
113845                     .data(commands)
113846                     .enter()
113847                     .append('button')
113848                     .attr('class', function(d) { return 'disabled ' + d.id + '-button bar-button'; })
113849                     .on('pointerup', function() {
113850                         // `pointerup` is always called before `click`
113851                         lastPointerUpType = event.pointerType;
113852                     })
113853                     .on('click', function(d) {
113854                         event.preventDefault();
113855
113856                         var annotation = d.annotation();
113857
113858                         if (editable() && annotation) {
113859                             d.action();
113860                         }
113861
113862                         if (editable() && (
113863                             lastPointerUpType === 'touch' ||
113864                             lastPointerUpType === 'pen')
113865                         ) {
113866                             // there are no tooltips for touch interactions so flash feedback instead
113867
113868                             var text = annotation ?
113869                                 _t(d.id + '.tooltip', { action: annotation }) :
113870                                 _t(d.id + '.nothing');
113871                             context.ui().flash
113872                                 .duration(2000)
113873                                 .iconName('#' + d.icon)
113874                                 .iconClass(annotation ? '' : 'disabled')
113875                                 .text(text)();
113876                         }
113877                         lastPointerUpType = null;
113878                     })
113879                     .call(tooltipBehavior);
113880
113881                 buttons.each(function(d) {
113882                     select(this)
113883                         .call(svgIcon('#' + d.icon));
113884                 });
113885
113886                 context.keybinding()
113887                     .on(commands[0].cmd, function() {
113888                         event.preventDefault();
113889                         if (editable()) commands[0].action();
113890                     })
113891                     .on(commands[1].cmd, function() {
113892                         event.preventDefault();
113893                         if (editable()) commands[1].action();
113894                     });
113895
113896
113897                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113898
113899                 context.map()
113900                     .on('move.undo_redo', debouncedUpdate)
113901                     .on('drawn.undo_redo', debouncedUpdate);
113902
113903                 context.history()
113904                     .on('change.undo_redo', function(difference) {
113905                         if (difference) update();
113906                     });
113907
113908                 context
113909                     .on('enter.undo_redo', update);
113910
113911
113912                 function update() {
113913                     buttons
113914                         .classed('disabled', function(d) {
113915                             return !editable() || !d.annotation();
113916                         })
113917                         .each(function() {
113918                             var selection = select(this);
113919                             if (!selection.select('.tooltip.in').empty()) {
113920                                 selection.call(tooltipBehavior.updateContent);
113921                             }
113922                         });
113923                 }
113924             };
113925
113926             tool.uninstall = function() {
113927                 context.keybinding()
113928                     .off(commands[0].cmd)
113929                     .off(commands[1].cmd);
113930
113931                 context.map()
113932                     .on('move.undo_redo', null)
113933                     .on('drawn.undo_redo', null);
113934
113935                 context.history()
113936                     .on('change.undo_redo', null);
113937
113938                 context
113939                     .on('enter.undo_redo', null);
113940             };
113941
113942             return tool;
113943         }
113944
113945         function uiTopToolbar(context) {
113946
113947             var sidebarToggle = uiToolSidebarToggle(context),
113948                 modes = uiToolOldDrawModes(context),
113949                 notes = uiToolNotes(context),
113950                 undoRedo = uiToolUndoRedo(context),
113951                 save = uiToolSave(context);
113952
113953             function notesEnabled() {
113954                 var noteLayer = context.layers().layer('notes');
113955                 return noteLayer && noteLayer.enabled();
113956             }
113957
113958             function topToolbar(bar) {
113959
113960                 bar.on('wheel.topToolbar', function() {
113961                     if (!event.deltaX) {
113962                         // translate vertical scrolling into horizontal scrolling in case
113963                         // the user doesn't have an input device that can scroll horizontally
113964                         bar.node().scrollLeft += event.deltaY;
113965                     }
113966                 });
113967
113968                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113969                 context.layers()
113970                     .on('change.topToolbar', debouncedUpdate);
113971
113972                 update();
113973
113974                 function update() {
113975
113976                     var tools = [
113977                         sidebarToggle,
113978                         'spacer',
113979                         modes
113980                     ];
113981
113982                     tools.push('spacer');
113983
113984                     if (notesEnabled()) {
113985                         tools = tools.concat([notes, 'spacer']);
113986                     }
113987
113988                     tools = tools.concat([undoRedo, save]);
113989
113990                     var toolbarItems = bar.selectAll('.toolbar-item')
113991                         .data(tools, function(d) {
113992                             return d.id || d;
113993                         });
113994
113995                     toolbarItems.exit()
113996                         .each(function(d) {
113997                             if (d.uninstall) {
113998                                 d.uninstall();
113999                             }
114000                         })
114001                         .remove();
114002
114003                     var itemsEnter = toolbarItems
114004                         .enter()
114005                         .append('div')
114006                         .attr('class', function(d) {
114007                             var classes = 'toolbar-item ' + (d.id || d).replace('_', '-');
114008                             if (d.klass) classes += ' ' + d.klass;
114009                             return classes;
114010                         });
114011
114012                     var actionableItems = itemsEnter.filter(function(d) { return d !== 'spacer'; });
114013
114014                     actionableItems
114015                         .append('div')
114016                         .attr('class', 'item-content')
114017                         .each(function(d) {
114018                             select(this).call(d.render, bar);
114019                         });
114020
114021                     actionableItems
114022                         .append('div')
114023                         .attr('class', 'item-label')
114024                         .text(function(d) {
114025                             return d.label;
114026                         });
114027                 }
114028
114029             }
114030
114031             return topToolbar;
114032         }
114033
114034         // these are module variables so they are preserved through a ui.restart()
114035         var sawVersion = null;
114036         var isNewVersion = false;
114037         var isNewUser = false;
114038
114039
114040         function uiVersion(context) {
114041
114042             var currVersion = context.version;
114043             var matchedVersion = currVersion.match(/\d+\.\d+\.\d+.*/);
114044
114045             if (sawVersion === null && matchedVersion !== null) {
114046                 if (corePreferences('sawVersion')) {
114047                     isNewUser = false;
114048                     isNewVersion = corePreferences('sawVersion') !== currVersion && currVersion.indexOf('-') === -1;
114049                 } else {
114050                     isNewUser = true;
114051                     isNewVersion = true;
114052                 }
114053                 corePreferences('sawVersion', currVersion);
114054                 sawVersion = currVersion;
114055             }
114056
114057             return function(selection) {
114058                 selection
114059                     .append('a')
114060                     .attr('target', '_blank')
114061                     .attr('href', 'https://github.com/openstreetmap/iD')
114062                     .text(currVersion);
114063
114064                 // only show new version indicator to users that have used iD before
114065                 if (isNewVersion && !isNewUser) {
114066                     selection
114067                         .append('div')
114068                         .attr('class', 'badge')
114069                         .append('a')
114070                         .attr('target', '_blank')
114071                         .attr('href', 'https://github.com/openstreetmap/iD/blob/release/CHANGELOG.md#whats-new')
114072                         .call(svgIcon('#maki-gift-11'))
114073                         .call(uiTooltip()
114074                             .title(_t('version.whats_new', { version: currVersion }))
114075                             .placement('top')
114076                         );
114077                 }
114078             };
114079         }
114080
114081         function uiZoom(context) {
114082
114083             var zooms = [{
114084                 id: 'zoom-in',
114085                 icon: 'iD-icon-plus',
114086                 title: _t('zoom.in'),
114087                 action: zoomIn,
114088                 disabled: function() {
114089                     return !context.map().canZoomIn();
114090                 },
114091                 disabledTitle: _t('zoom.disabled.in'),
114092                 key: '+'
114093             }, {
114094                 id: 'zoom-out',
114095                 icon: 'iD-icon-minus',
114096                 title: _t('zoom.out'),
114097                 action: zoomOut,
114098                 disabled: function() {
114099                     return !context.map().canZoomOut();
114100                 },
114101                 disabledTitle: _t('zoom.disabled.out'),
114102                 key: '-'
114103             }];
114104
114105             function zoomIn() {
114106                 event.preventDefault();
114107                 context.map().zoomIn();
114108             }
114109
114110             function zoomOut() {
114111                 event.preventDefault();
114112                 context.map().zoomOut();
114113             }
114114
114115             function zoomInFurther() {
114116                 event.preventDefault();
114117                 context.map().zoomInFurther();
114118             }
114119
114120             function zoomOutFurther() {
114121                 event.preventDefault();
114122                 context.map().zoomOutFurther();
114123             }
114124
114125             return function(selection) {
114126                 var tooltipBehavior = uiTooltip()
114127                     .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114128                     .title(function(d) {
114129                         if (d.disabled()) {
114130                             return d.disabledTitle;
114131                         }
114132                         return d.title;
114133                     })
114134                     .keys(function(d) {
114135                         return [d.key];
114136                     });
114137
114138                 var lastPointerUpType;
114139
114140                 var buttons = selection.selectAll('button')
114141                     .data(zooms)
114142                     .enter()
114143                     .append('button')
114144                     .attr('class', function(d) { return d.id; })
114145                     .on('pointerup.editor', function() {
114146                         lastPointerUpType = event.pointerType;
114147                     })
114148                     .on('click.editor', function(d) {
114149                         if (!d.disabled()) {
114150                             d.action();
114151                         } else if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
114152                             context.ui().flash
114153                                 .duration(2000)
114154                                 .iconName('#' + d.icon)
114155                                 .iconClass('disabled')
114156                                 .text(d.disabledTitle)();
114157                         }
114158                         lastPointerUpType = null;
114159                     })
114160                     .call(tooltipBehavior);
114161
114162                 buttons.each(function(d) {
114163                     select(this)
114164                         .call(svgIcon('#' + d.icon, 'light'));
114165                 });
114166
114167                 ['plus', 'ffplus', '=', 'ffequals'].forEach(function(key) {
114168                     context.keybinding().on([key], zoomIn);
114169                     context.keybinding().on([uiCmd('⌘' + key)], zoomInFurther);
114170                 });
114171
114172                 ['_', '-', 'ffminus', 'dash'].forEach(function(key) {
114173                     context.keybinding().on([key], zoomOut);
114174                     context.keybinding().on([uiCmd('⌘' + key)], zoomOutFurther);
114175                 });
114176
114177                 function updateButtonStates() {
114178                     buttons
114179                         .classed('disabled', function(d) {
114180                             return d.disabled();
114181                         })
114182                         .each(function() {
114183                             var selection = select(this);
114184                             if (!selection.select('.tooltip.in').empty()) {
114185                                 selection.call(tooltipBehavior.updateContent);
114186                             }
114187                         });
114188                 }
114189
114190                 updateButtonStates();
114191
114192                 context.map().on('move.uiZoom', updateButtonStates);
114193             };
114194         }
114195
114196         function uiZoomToSelection(context) {
114197
114198             function isDisabled() {
114199                 var mode = context.mode();
114200                 return !mode || !mode.zoomToSelected;
114201             }
114202
114203             var _lastPointerUpType;
114204
114205             function pointerup() {
114206                 _lastPointerUpType = event.pointerType;
114207             }
114208
114209             function click() {
114210                 event.preventDefault();
114211
114212                 if (isDisabled()) {
114213                     if (_lastPointerUpType === 'touch' || _lastPointerUpType === 'pen') {
114214                         context.ui().flash
114215                             .duration(2000)
114216                             .iconName('#iD-icon-framed-dot')
114217                             .iconClass('disabled')
114218                             .text(_t('inspector.zoom_to.no_selection'))();
114219                     }
114220                 } else {
114221                     var mode = context.mode();
114222                     if (mode && mode.zoomToSelected) {
114223                         mode.zoomToSelected();
114224                     }
114225                 }
114226
114227                 _lastPointerUpType = null;
114228             }
114229
114230             return function(selection) {
114231
114232                 var tooltipBehavior = uiTooltip()
114233                     .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114234                     .title(function() {
114235                         if (isDisabled()) {
114236                             return _t('inspector.zoom_to.no_selection');
114237                         }
114238                         return _t('inspector.zoom_to.title');
114239                     })
114240                     .keys([_t('inspector.zoom_to.key')]);
114241
114242                 var button = selection
114243                     .append('button')
114244                     .on('pointerup', pointerup)
114245                     .on('click', click)
114246                     .call(svgIcon('#iD-icon-framed-dot', 'light'))
114247                     .call(tooltipBehavior);
114248
114249                 function setEnabledState() {
114250                     button.classed('disabled', isDisabled());
114251                     if (!button.select('.tooltip.in').empty()) {
114252                         button.call(tooltipBehavior.updateContent);
114253                     }
114254                 }
114255
114256                 context.on('enter.uiZoomToSelection', setEnabledState);
114257
114258                 setEnabledState();
114259             };
114260         }
114261
114262         function uiPane(id, context) {
114263
114264             var _key;
114265             var _title = '';
114266             var _description = '';
114267             var _iconName = '';
114268             var _sections; // array of uiSection objects
114269
114270             var _paneSelection = select(null);
114271
114272             var _paneTooltip;
114273
114274             var pane = {
114275                 id: id
114276             };
114277
114278             pane.title = function(val) {
114279                 if (!arguments.length) return _title;
114280                 _title = val;
114281                 return pane;
114282             };
114283
114284             pane.key = function(val) {
114285                 if (!arguments.length) return _key;
114286                 _key = val;
114287                 return pane;
114288             };
114289
114290             pane.description = function(val) {
114291                 if (!arguments.length) return _description;
114292                 _description = val;
114293                 return pane;
114294             };
114295
114296             pane.iconName = function(val) {
114297                 if (!arguments.length) return _iconName;
114298                 _iconName = val;
114299                 return pane;
114300             };
114301
114302             pane.sections = function(val) {
114303                 if (!arguments.length) return _sections;
114304                 _sections = val;
114305                 return pane;
114306             };
114307
114308             pane.selection = function() {
114309                 return _paneSelection;
114310             };
114311
114312             function hidePane() {
114313                 context.ui().togglePanes();
114314             }
114315
114316             pane.togglePane = function() {
114317                 if (event) event.preventDefault();
114318                 _paneTooltip.hide();
114319                 context.ui().togglePanes(!_paneSelection.classed('shown') ? _paneSelection : undefined);
114320             };
114321
114322             pane.renderToggleButton = function(selection) {
114323
114324                 if (!_paneTooltip) {
114325                     _paneTooltip = uiTooltip()
114326                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114327                         .title(_description)
114328                         .keys([_key]);
114329                 }
114330
114331                 selection
114332                     .append('button')
114333                     .on('click', pane.togglePane)
114334                     .call(svgIcon('#' + _iconName, 'light'))
114335                     .call(_paneTooltip);
114336             };
114337
114338             pane.renderContent = function(selection) {
114339                 // override to fully customize content
114340
114341                 if (_sections) {
114342                     _sections.forEach(function(section) {
114343                         selection.call(section.render);
114344                     });
114345                 }
114346             };
114347
114348             pane.renderPane = function(selection) {
114349
114350                 _paneSelection = selection
114351                     .append('div')
114352                     .attr('class', 'fillL map-pane hide ' + id + '-pane')
114353                     .attr('pane', id);
114354
114355                 var heading = _paneSelection
114356                     .append('div')
114357                     .attr('class', 'pane-heading');
114358
114359                 heading
114360                     .append('h2')
114361                     .text(_title);
114362
114363                 heading
114364                     .append('button')
114365                     .on('click', hidePane)
114366                     .call(svgIcon('#iD-icon-close'));
114367
114368
114369                 _paneSelection
114370                     .append('div')
114371                     .attr('class', 'pane-content')
114372                     .call(pane.renderContent);
114373
114374                 if (_key) {
114375                     context.keybinding()
114376                         .on(_key, pane.togglePane);
114377                 }
114378             };
114379
114380             return pane;
114381         }
114382
114383         function uiSectionBackgroundDisplayOptions(context) {
114384
114385             var section = uiSection('background-display-options', context)
114386                 .title(_t('background.display_options'))
114387                 .disclosureContent(renderDisclosureContent);
114388
114389             var _detected = utilDetect();
114390             var _storedOpacity = corePreferences('background-opacity');
114391             var _minVal = 0.25;
114392             var _maxVal = _detected.cssfilters ? 2 : 1;
114393
114394             var _sliders = _detected.cssfilters
114395                 ? ['brightness', 'contrast', 'saturation', 'sharpness']
114396                 : ['brightness'];
114397
114398             var _options = {
114399                 brightness: (_storedOpacity !== null ? (+_storedOpacity) : 1),
114400                 contrast: 1,
114401                 saturation: 1,
114402                 sharpness: 1
114403             };
114404
114405             function clamp(x, min, max) {
114406                 return Math.max(min, Math.min(x, max));
114407             }
114408
114409             function updateValue(d, val) {
114410                 if (!val && event && event.target) {
114411                     val = event.target.value;
114412                 }
114413
114414                 val = clamp(val, _minVal, _maxVal);
114415
114416                 _options[d] = val;
114417                 context.background()[d](val);
114418
114419                 if (d === 'brightness') {
114420                     corePreferences('background-opacity', val);
114421                 }
114422
114423                 section.reRender();
114424             }
114425
114426             function renderDisclosureContent(selection) {
114427                 var container = selection.selectAll('.display-options-container')
114428                     .data([0]);
114429
114430                 var containerEnter = container.enter()
114431                     .append('div')
114432                     .attr('class', 'display-options-container controls-list');
114433
114434                 // add slider controls
114435                 var slidersEnter = containerEnter.selectAll('.display-control')
114436                     .data(_sliders)
114437                     .enter()
114438                     .append('div')
114439                     .attr('class', function(d) { return 'display-control display-control-' + d; });
114440
114441                 slidersEnter
114442                     .append('h5')
114443                     .text(function(d) { return _t('background.' + d); })
114444                     .append('span')
114445                     .attr('class', function(d) { return 'display-option-value display-option-value-' + d; });
114446
114447                 slidersEnter
114448                     .append('input')
114449                     .attr('class', function(d) { return 'display-option-input display-option-input-' + d; })
114450                     .attr('type', 'range')
114451                     .attr('min', _minVal)
114452                     .attr('max', _maxVal)
114453                     .attr('step', '0.05')
114454                     .on('input', function(d) {
114455                         var val = select(this).property('value');
114456                         updateValue(d, val);
114457                     });
114458
114459                 slidersEnter
114460                     .append('button')
114461                     .attr('title', _t('background.reset'))
114462                     .attr('class', function(d) { return 'display-option-reset display-option-reset-' + d; })
114463                     .on('click', function(d) {
114464                         if (event.button !== 0) return;
114465                         updateValue(d, 1);
114466                     })
114467                     .call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
114468
114469                 // reset all button
114470                 containerEnter
114471                     .append('a')
114472                     .attr('class', 'display-option-resetlink')
114473                     .attr('href', '#')
114474                     .text(_t('background.reset_all'))
114475                     .on('click', function() {
114476                         for (var i = 0; i < _sliders.length; i++) {
114477                             updateValue(_sliders[i],1);
114478                         }
114479                     });
114480
114481                 // update
114482                 container = containerEnter
114483                     .merge(container);
114484
114485                 container.selectAll('.display-option-input')
114486                     .property('value', function(d) { return _options[d]; });
114487
114488                 container.selectAll('.display-option-value')
114489                     .text(function(d) { return Math.floor(_options[d] * 100) + '%'; });
114490
114491                 container.selectAll('.display-option-reset')
114492                     .classed('disabled', function(d) { return _options[d] === 1; });
114493
114494                 // first time only, set brightness if needed
114495                 if (containerEnter.size() && _options.brightness !== 1) {
114496                     context.background().brightness(_options.brightness);
114497                 }
114498             }
114499
114500             return section;
114501         }
114502
114503         function uiSettingsCustomBackground() {
114504             var dispatch$1 = dispatch('change');
114505
114506             function render(selection) {
114507                 // keep separate copies of original and current settings
114508                 var _origSettings = {
114509                     template: corePreferences('background-custom-template')
114510                 };
114511                 var _currSettings = {
114512                     template: corePreferences('background-custom-template')
114513                 };
114514
114515                 var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
114516                 var modal = uiConfirm(selection).okButton();
114517
114518                 modal
114519                     .classed('settings-modal settings-custom-background', true);
114520
114521                 modal.select('.modal-section.header')
114522                     .append('h3')
114523                     .text(_t('settings.custom_background.header'));
114524
114525
114526                 var textSection = modal.select('.modal-section.message-text');
114527
114528                 var instructions =
114529                     `${_t('settings.custom_background.instructions.info')}\n` +
114530                     '\n' +
114531                     `#### ${_t('settings.custom_background.instructions.wms.tokens_label')}\n` +
114532                     `* ${_t('settings.custom_background.instructions.wms.tokens.proj')}\n` +
114533                     `* ${_t('settings.custom_background.instructions.wms.tokens.wkid')}\n` +
114534                     `* ${_t('settings.custom_background.instructions.wms.tokens.dimensions')}\n` +
114535                     `* ${_t('settings.custom_background.instructions.wms.tokens.bbox')}\n` +
114536                     '\n' +
114537                     `#### ${_t('settings.custom_background.instructions.tms.tokens_label')}\n` +
114538                     `* ${_t('settings.custom_background.instructions.tms.tokens.xyz')}\n` +
114539                     `* ${_t('settings.custom_background.instructions.tms.tokens.flipped_y')}\n` +
114540                     `* ${_t('settings.custom_background.instructions.tms.tokens.switch')}\n` +
114541                     `* ${_t('settings.custom_background.instructions.tms.tokens.quadtile')}\n` +
114542                     `* ${_t('settings.custom_background.instructions.tms.tokens.scale_factor')}\n` +
114543                     '\n' +
114544                     `#### ${_t('settings.custom_background.instructions.example')}\n` +
114545                     `\`${example}\``;
114546
114547                 textSection
114548                     .append('div')
114549                     .attr('class', 'instructions-template')
114550                     .html(marked_1(instructions));
114551
114552                 textSection
114553                     .append('textarea')
114554                     .attr('class', 'field-template')
114555                     .attr('placeholder', _t('settings.custom_background.template.placeholder'))
114556                     .call(utilNoAuto)
114557                     .property('value', _currSettings.template);
114558
114559
114560                 // insert a cancel button
114561                 var buttonSection = modal.select('.modal-section.buttons');
114562
114563                 buttonSection
114564                     .insert('button', '.ok-button')
114565                     .attr('class', 'button cancel-button secondary-action')
114566                     .text(_t('confirm.cancel'));
114567
114568
114569                 buttonSection.select('.cancel-button')
114570                     .on('click.cancel', clickCancel);
114571
114572                 buttonSection.select('.ok-button')
114573                     .attr('disabled', isSaveDisabled)
114574                     .on('click.save', clickSave);
114575
114576
114577                 function isSaveDisabled() {
114578                     return null;
114579                 }
114580
114581
114582                 // restore the original template
114583                 function clickCancel() {
114584                     textSection.select('.field-template').property('value', _origSettings.template);
114585                     corePreferences('background-custom-template', _origSettings.template);
114586                     this.blur();
114587                     modal.close();
114588                 }
114589
114590                 // accept the current template
114591                 function clickSave() {
114592                     _currSettings.template = textSection.select('.field-template').property('value');
114593                     corePreferences('background-custom-template', _currSettings.template);
114594                     this.blur();
114595                     modal.close();
114596                     dispatch$1.call('change', this, _currSettings);
114597                 }
114598             }
114599
114600             return utilRebind(render, dispatch$1, 'on');
114601         }
114602
114603         function uiSectionBackgroundList(context) {
114604
114605             var _backgroundList = select(null);
114606
114607             var _customSource = context.background().findSource('custom');
114608
114609             var _settingsCustomBackground = uiSettingsCustomBackground()
114610                 .on('change', customChanged);
114611
114612             var section = uiSection('background-list', context)
114613                 .title(_t('background.backgrounds'))
114614                 .disclosureContent(renderDisclosureContent);
114615
114616             function previousBackgroundID() {
114617                 return corePreferences('background-last-used-toggle');
114618             }
114619
114620             function renderDisclosureContent(selection) {
114621
114622                 // the background list
114623                 var container = selection.selectAll('.layer-background-list')
114624                     .data([0]);
114625
114626                 _backgroundList = container.enter()
114627                     .append('ul')
114628                     .attr('class', 'layer-list layer-background-list')
114629                     .attr('dir', 'auto')
114630                     .merge(container);
114631
114632
114633                 // add minimap toggle below list
114634                 var bgExtrasListEnter = selection.selectAll('.bg-extras-list')
114635                     .data([0])
114636                     .enter()
114637                     .append('ul')
114638                     .attr('class', 'layer-list bg-extras-list');
114639
114640                 var minimapLabelEnter = bgExtrasListEnter
114641                     .append('li')
114642                     .attr('class', 'minimap-toggle-item')
114643                     .append('label')
114644                     .call(uiTooltip()
114645                         .title(_t('background.minimap.tooltip'))
114646                         .keys([_t('background.minimap.key')])
114647                         .placement('top')
114648                     );
114649
114650                 minimapLabelEnter
114651                     .append('input')
114652                     .attr('type', 'checkbox')
114653                     .on('change', function() {
114654                         event.preventDefault();
114655                         uiMapInMap.toggle();
114656                     });
114657
114658                 minimapLabelEnter
114659                     .append('span')
114660                     .text(_t('background.minimap.description'));
114661
114662
114663                 var panelLabelEnter = bgExtrasListEnter
114664                     .append('li')
114665                     .attr('class', 'background-panel-toggle-item')
114666                     .append('label')
114667                     .call(uiTooltip()
114668                         .title(_t('background.panel.tooltip'))
114669                         .keys([uiCmd('⌘⇧' + _t('info_panels.background.key'))])
114670                         .placement('top')
114671                     );
114672
114673                 panelLabelEnter
114674                     .append('input')
114675                     .attr('type', 'checkbox')
114676                     .on('change', function() {
114677                         event.preventDefault();
114678                         context.ui().info.toggle('background');
114679                     });
114680
114681                 panelLabelEnter
114682                     .append('span')
114683                     .text(_t('background.panel.description'));
114684
114685                 var locPanelLabelEnter = bgExtrasListEnter
114686                     .append('li')
114687                     .attr('class', 'location-panel-toggle-item')
114688                     .append('label')
114689                     .call(uiTooltip()
114690                         .title(_t('background.location_panel.tooltip'))
114691                         .keys([uiCmd('⌘⇧' + _t('info_panels.location.key'))])
114692                         .placement('top')
114693                     );
114694
114695                 locPanelLabelEnter
114696                     .append('input')
114697                     .attr('type', 'checkbox')
114698                     .on('change', function() {
114699                         event.preventDefault();
114700                         context.ui().info.toggle('location');
114701                     });
114702
114703                 locPanelLabelEnter
114704                     .append('span')
114705                     .text(_t('background.location_panel.description'));
114706
114707
114708                 // "Info / Report a Problem" link
114709                 selection.selectAll('.imagery-faq')
114710                     .data([0])
114711                     .enter()
114712                     .append('div')
114713                     .attr('class', 'imagery-faq')
114714                     .append('a')
114715                     .attr('target', '_blank')
114716                     .call(svgIcon('#iD-icon-out-link', 'inline'))
114717                     .attr('href', 'https://github.com/openstreetmap/iD/blob/develop/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
114718                     .append('span')
114719                     .text(_t('background.imagery_problem_faq'));
114720
114721                 _backgroundList
114722                     .call(drawListItems, 'radio', chooseBackground, function(d) { return !d.isHidden() && !d.overlay; });
114723             }
114724
114725             function setTooltips(selection) {
114726                 selection.each(function(d, i, nodes) {
114727                     var item = select(this).select('label');
114728                     var span = item.select('span');
114729                     var placement = (i < nodes.length / 2) ? 'bottom' : 'top';
114730                     var description = d.description();
114731                     var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
114732
114733                     item.call(uiTooltip().destroyAny);
114734
114735                     if (d.id === previousBackgroundID()) {
114736                         item.call(uiTooltip()
114737                             .placement(placement)
114738                             .title('<div>' + _t('background.switch') + '</div>')
114739                             .keys([uiCmd('⌘' + _t('background.key'))])
114740                         );
114741                     } else if (description || isOverflowing) {
114742                         item.call(uiTooltip()
114743                             .placement(placement)
114744                             .title(description || d.name())
114745                         );
114746                     }
114747                 });
114748             }
114749
114750             function drawListItems(layerList, type, change, filter) {
114751                 var sources = context.background()
114752                     .sources(context.map().extent(), context.map().zoom(), true)
114753                     .filter(filter);
114754
114755                 var layerLinks = layerList.selectAll('li')
114756                     .data(sources, function(d) { return d.name(); });
114757
114758                 layerLinks.exit()
114759                     .remove();
114760
114761                 var enter = layerLinks.enter()
114762                     .append('li')
114763                     .classed('layer-custom', function(d) { return d.id === 'custom'; })
114764                     .classed('best', function(d) { return d.best(); });
114765
114766                 var label = enter
114767                     .append('label');
114768
114769                 label
114770                     .append('input')
114771                     .attr('type', type)
114772                     .attr('name', 'layers')
114773                     .on('change', change);
114774
114775                 label
114776                     .append('span')
114777                     .text(function(d) { return d.name(); });
114778
114779                 enter.filter(function(d) { return d.id === 'custom'; })
114780                     .append('button')
114781                     .attr('class', 'layer-browse')
114782                     .call(uiTooltip()
114783                         .title(_t('settings.custom_background.tooltip'))
114784                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114785                     )
114786                     .on('click', editCustom)
114787                     .call(svgIcon('#iD-icon-more'));
114788
114789                 enter.filter(function(d) { return d.best(); })
114790                     .append('div')
114791                     .attr('class', 'best')
114792                     .call(uiTooltip()
114793                         .title(_t('background.best_imagery'))
114794                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114795                     )
114796                     .append('span')
114797                     .html('&#9733;');
114798
114799
114800                 layerList.selectAll('li')
114801                     .sort(sortSources);
114802
114803                 layerList
114804                     .call(updateLayerSelections);
114805
114806
114807                 function sortSources(a, b) {
114808                     return a.best() && !b.best() ? -1
114809                         : b.best() && !a.best() ? 1
114810                         : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
114811                 }
114812             }
114813
114814             function updateLayerSelections(selection) {
114815                 function active(d) {
114816                     return context.background().showsLayer(d);
114817                 }
114818
114819                 selection.selectAll('li')
114820                     .classed('active', active)
114821                     .classed('switch', function(d) { return d.id === previousBackgroundID(); })
114822                     .call(setTooltips)
114823                     .selectAll('input')
114824                     .property('checked', active);
114825             }
114826
114827
114828             function chooseBackground(d) {
114829                 if (d.id === 'custom' && !d.template()) {
114830                     return editCustom();
114831                 }
114832
114833                 event.preventDefault();
114834                 var previousBackground = context.background().baseLayerSource();
114835                 corePreferences('background-last-used-toggle', previousBackground.id);
114836                 corePreferences('background-last-used', d.id);
114837                 context.background().baseLayerSource(d);
114838                 document.activeElement.blur();
114839             }
114840
114841
114842             function customChanged(d) {
114843                 if (d && d.template) {
114844                     _customSource.template(d.template);
114845                     chooseBackground(_customSource);
114846                 } else {
114847                     _customSource.template('');
114848                     chooseBackground(context.background().findSource('none'));
114849                 }
114850             }
114851
114852
114853             function editCustom() {
114854                 event.preventDefault();
114855                 context.container()
114856                     .call(_settingsCustomBackground);
114857             }
114858
114859
114860             context.background()
114861                 .on('change.background_list', function() {
114862                     _backgroundList.call(updateLayerSelections);
114863                 });
114864
114865             context.map()
114866                 .on('move.background_list',
114867                     debounce(function() {
114868                         // layers in-view may have changed due to map move
114869                         window.requestIdleCallback(section.reRender);
114870                     }, 1000)
114871                 );
114872
114873             return section;
114874         }
114875
114876         function uiSectionBackgroundOffset(context) {
114877
114878             var section = uiSection('background-offset', context)
114879                 .title(_t('background.fix_misalignment'))
114880                 .disclosureContent(renderDisclosureContent)
114881                 .expandedByDefault(false);
114882
114883             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
114884
114885             var _directions = [
114886                 ['right', [0.5, 0]],
114887                 ['top', [0, -0.5]],
114888                 ['left', [-0.5, 0]],
114889                 ['bottom', [0, 0.5]]
114890             ];
114891
114892
114893             function cancelEvent() {
114894                 event.stopPropagation();
114895                 event.preventDefault();
114896             }
114897
114898
114899             function updateValue() {
114900                 var meters = geoOffsetToMeters(context.background().offset());
114901                 var x = +meters[0].toFixed(2);
114902                 var y = +meters[1].toFixed(2);
114903
114904                 context.container().selectAll('.nudge-inner-rect')
114905                     .select('input')
114906                     .classed('error', false)
114907                     .property('value', x + ', ' + y);
114908
114909                 context.container().selectAll('.nudge-reset')
114910                     .classed('disabled', function() {
114911                         return (x === 0 && y === 0);
114912                     });
114913             }
114914
114915
114916             function resetOffset() {
114917                 context.background().offset([0, 0]);
114918                 updateValue();
114919             }
114920
114921
114922             function nudge(d) {
114923                 context.background().nudge(d, context.map().zoom());
114924                 updateValue();
114925             }
114926
114927
114928             function pointerdownNudgeButton(d) {
114929                 var interval;
114930                 var timeout = window.setTimeout(function() {
114931                         interval = window.setInterval(nudge.bind(null, d), 100);
114932                     }, 500);
114933
114934                 function doneNudge() {
114935                     window.clearTimeout(timeout);
114936                     window.clearInterval(interval);
114937                     select(window)
114938                         .on(_pointerPrefix + 'up.buttonoffset', null, true)
114939                         .on(_pointerPrefix + 'down.buttonoffset', null, true);
114940                 }
114941
114942                 select(window)
114943                     .on(_pointerPrefix + 'up.buttonoffset', doneNudge, true)
114944                     .on(_pointerPrefix + 'down.buttonoffset', doneNudge, true);
114945
114946                 nudge(d);
114947             }
114948
114949
114950             function inputOffset() {
114951                 var input = select(this);
114952                 var d = input.node().value;
114953
114954                 if (d === '') return resetOffset();
114955
114956                 d = d.replace(/;/g, ',').split(',').map(function(n) {
114957                     // if n is NaN, it will always get mapped to false.
114958                     return !isNaN(n) && n;
114959                 });
114960
114961                 if (d.length !== 2 || !d[0] || !d[1]) {
114962                     input.classed('error', true);
114963                     return;
114964                 }
114965
114966                 context.background().offset(geoMetersToOffset(d));
114967                 updateValue();
114968             }
114969
114970
114971             function dragOffset() {
114972                 if (event.button !== 0) return;
114973
114974                 var origin = [event.clientX, event.clientY];
114975
114976                 var pointerId = event.pointerId || 'mouse';
114977
114978                 context.container()
114979                     .append('div')
114980                     .attr('class', 'nudge-surface');
114981
114982                 select(window)
114983                     .on(_pointerPrefix + 'move.drag-bg-offset', pointermove)
114984                     .on(_pointerPrefix + 'up.drag-bg-offset', pointerup);
114985
114986                 if (_pointerPrefix === 'pointer') {
114987                     select(window)
114988                         .on('pointercancel.drag-bg-offset', pointerup);
114989                 }
114990
114991                 function pointermove() {
114992                     if (pointerId !== (event.pointerId || 'mouse')) return;
114993
114994                     var latest = [event.clientX, event.clientY];
114995                     var d = [
114996                         -(origin[0] - latest[0]) / 4,
114997                         -(origin[1] - latest[1]) / 4
114998                     ];
114999
115000                     origin = latest;
115001                     nudge(d);
115002                 }
115003
115004                 function pointerup() {
115005                     if (pointerId !== (event.pointerId || 'mouse')) return;
115006                     if (event.button !== 0) return;
115007
115008                     context.container().selectAll('.nudge-surface')
115009                         .remove();
115010
115011                     select(window)
115012                         .on('.drag-bg-offset', null);
115013                 }
115014             }
115015
115016
115017             function renderDisclosureContent(selection) {
115018                 var container = selection.selectAll('.nudge-container')
115019                     .data([0]);
115020
115021                 var containerEnter = container.enter()
115022                     .append('div')
115023                     .attr('class', 'nudge-container cf');
115024
115025                 containerEnter
115026                     .append('div')
115027                     .attr('class', 'nudge-instructions')
115028                     .text(_t('background.offset'));
115029
115030                 var nudgeEnter = containerEnter
115031                     .append('div')
115032                     .attr('class', 'nudge-outer-rect')
115033                     .on(_pointerPrefix + 'down', dragOffset);
115034
115035                 nudgeEnter
115036                     .append('div')
115037                     .attr('class', 'nudge-inner-rect')
115038                     .append('input')
115039                     .on('change', inputOffset);
115040
115041                 containerEnter
115042                     .append('div')
115043                     .selectAll('button')
115044                     .data(_directions).enter()
115045                     .append('button')
115046                     .attr('class', function(d) { return d[0] + ' nudge'; })
115047                     .on('contextmenu', cancelEvent)
115048                     .on(_pointerPrefix + 'down', function(d) {
115049                         if (event.button !== 0) return;
115050                         pointerdownNudgeButton(d[1]);
115051                     });
115052
115053                 containerEnter
115054                     .append('button')
115055                     .attr('title', _t('background.reset'))
115056                     .attr('class', 'nudge-reset disabled')
115057                     .on('contextmenu', cancelEvent)
115058                     .on('click', function() {
115059                         event.preventDefault();
115060                         if (event.button !== 0) return;
115061                         resetOffset();
115062                     })
115063                     .call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
115064
115065                 updateValue();
115066             }
115067
115068             context.background()
115069                 .on('change.backgroundOffset-update', updateValue);
115070
115071             return section;
115072         }
115073
115074         function uiSectionOverlayList(context) {
115075
115076             var section = uiSection('overlay-list', context)
115077                 .title(_t('background.overlays'))
115078                 .disclosureContent(renderDisclosureContent);
115079
115080             var _overlayList = select(null);
115081
115082             function setTooltips(selection) {
115083                 selection.each(function(d, i, nodes) {
115084                     var item = select(this).select('label');
115085                     var span = item.select('span');
115086                     var placement = (i < nodes.length / 2) ? 'bottom' : 'top';
115087                     var description = d.description();
115088                     var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
115089
115090                     item.call(uiTooltip().destroyAny);
115091
115092                     if (description || isOverflowing) {
115093                         item.call(uiTooltip()
115094                             .placement(placement)
115095                             .title(description || d.name())
115096                         );
115097                     }
115098                 });
115099             }
115100
115101             function updateLayerSelections(selection) {
115102                 function active(d) {
115103                     return context.background().showsLayer(d);
115104                 }
115105
115106                 selection.selectAll('li')
115107                     .classed('active', active)
115108                     .call(setTooltips)
115109                     .selectAll('input')
115110                     .property('checked', active);
115111             }
115112
115113
115114             function chooseOverlay(d) {
115115                 event.preventDefault();
115116                 context.background().toggleOverlayLayer(d);
115117                 _overlayList.call(updateLayerSelections);
115118                 document.activeElement.blur();
115119             }
115120
115121             function drawListItems(layerList, type, change, filter) {
115122                 var sources = context.background()
115123                     .sources(context.map().extent(), context.map().zoom(), true)
115124                     .filter(filter);
115125
115126                 var layerLinks = layerList.selectAll('li')
115127                     .data(sources, function(d) { return d.name(); });
115128
115129                 layerLinks.exit()
115130                     .remove();
115131
115132                 var enter = layerLinks.enter()
115133                     .append('li');
115134
115135                 var label = enter
115136                     .append('label');
115137
115138                 label
115139                     .append('input')
115140                     .attr('type', type)
115141                     .attr('name', 'layers')
115142                     .on('change', change);
115143
115144                 label
115145                     .append('span')
115146                     .text(function(d) { return d.name(); });
115147
115148
115149                 layerList.selectAll('li')
115150                     .sort(sortSources);
115151
115152                 layerList
115153                     .call(updateLayerSelections);
115154
115155
115156                 function sortSources(a, b) {
115157                     return a.best() && !b.best() ? -1
115158                         : b.best() && !a.best() ? 1
115159                         : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
115160                 }
115161             }
115162
115163             function renderDisclosureContent(selection) {
115164
115165                 var container = selection.selectAll('.layer-overlay-list')
115166                     .data([0]);
115167
115168                 _overlayList = container.enter()
115169                     .append('ul')
115170                     .attr('class', 'layer-list layer-overlay-list')
115171                     .attr('dir', 'auto')
115172                     .merge(container);
115173
115174                 _overlayList
115175                     .call(drawListItems, 'checkbox', chooseOverlay, function(d) { return !d.isHidden() && d.overlay; });
115176             }
115177
115178             context.map()
115179                 .on('move.overlay_list',
115180                     debounce(function() {
115181                         // layers in-view may have changed due to map move
115182                         window.requestIdleCallback(section.reRender);
115183                     }, 1000)
115184                 );
115185
115186             return section;
115187         }
115188
115189         function uiPaneBackground(context) {
115190
115191             var backgroundPane = uiPane('background', context)
115192                 .key(_t('background.key'))
115193                 .title(_t('background.title'))
115194                 .description(_t('background.description'))
115195                 .iconName('iD-icon-layers')
115196                 .sections([
115197                     uiSectionBackgroundList(context),
115198                     uiSectionOverlayList(context),
115199                     uiSectionBackgroundDisplayOptions(context),
115200                     uiSectionBackgroundOffset(context)
115201                 ]);
115202
115203             return backgroundPane;
115204         }
115205
115206         function uiPaneHelp(context) {
115207
115208             var docKeys = [
115209                 ['help', [
115210                     'welcome',
115211                     'open_data_h',
115212                     'open_data',
115213                     'before_start_h',
115214                     'before_start',
115215                     'open_source_h',
115216                     'open_source',
115217                     'open_source_help'
115218                 ]],
115219                 ['overview', [
115220                     'navigation_h',
115221                     'navigation_drag',
115222                     'navigation_zoom',
115223                     'features_h',
115224                     'features',
115225                     'nodes_ways'
115226                 ]],
115227                 ['editing', [
115228                     'select_h',
115229                     'select_left_click',
115230                     'select_right_click',
115231                     'select_space',
115232                     'multiselect_h',
115233                     'multiselect',
115234                     'multiselect_shift_click',
115235                     'multiselect_lasso',
115236                     'undo_redo_h',
115237                     'undo_redo',
115238                     'save_h',
115239                     'save',
115240                     'save_validation',
115241                     'upload_h',
115242                     'upload',
115243                     'backups_h',
115244                     'backups',
115245                     'keyboard_h',
115246                     'keyboard'
115247                 ]],
115248                 ['feature_editor', [
115249                     'intro',
115250                     'definitions',
115251                     'type_h',
115252                     'type',
115253                     'type_picker',
115254                     'fields_h',
115255                     'fields_all_fields',
115256                     'fields_example',
115257                     'fields_add_field',
115258                     'tags_h',
115259                     'tags_all_tags',
115260                     'tags_resources'
115261                 ]],
115262                 ['points', [
115263                     'intro',
115264                     'add_point_h',
115265                     'add_point',
115266                     'add_point_finish',
115267                     'move_point_h',
115268                     'move_point',
115269                     'delete_point_h',
115270                     'delete_point',
115271                     'delete_point_command'
115272                 ]],
115273                 ['lines', [
115274                     'intro',
115275                     'add_line_h',
115276                     'add_line',
115277                     'add_line_draw',
115278                     'add_line_continue',
115279                     'add_line_finish',
115280                     'modify_line_h',
115281                     'modify_line_dragnode',
115282                     'modify_line_addnode',
115283                     'connect_line_h',
115284                     'connect_line',
115285                     'connect_line_display',
115286                     'connect_line_drag',
115287                     'connect_line_tag',
115288                     'disconnect_line_h',
115289                     'disconnect_line_command',
115290                     'move_line_h',
115291                     'move_line_command',
115292                     'move_line_connected',
115293                     'delete_line_h',
115294                     'delete_line',
115295                     'delete_line_command'
115296                 ]],
115297                 ['areas', [
115298                     'intro',
115299                     'point_or_area_h',
115300                     'point_or_area',
115301                     'add_area_h',
115302                     'add_area_command',
115303                     'add_area_draw',
115304                     'add_area_continue',
115305                     'add_area_finish',
115306                     'square_area_h',
115307                     'square_area_command',
115308                     'modify_area_h',
115309                     'modify_area_dragnode',
115310                     'modify_area_addnode',
115311                     'delete_area_h',
115312                     'delete_area',
115313                     'delete_area_command'
115314                 ]],
115315                 ['relations', [
115316                     'intro',
115317                     'edit_relation_h',
115318                     'edit_relation',
115319                     'edit_relation_add',
115320                     'edit_relation_delete',
115321                     'maintain_relation_h',
115322                     'maintain_relation',
115323                     'relation_types_h',
115324                     'multipolygon_h',
115325                     'multipolygon',
115326                     'multipolygon_create',
115327                     'multipolygon_merge',
115328                     'turn_restriction_h',
115329                     'turn_restriction',
115330                     'turn_restriction_field',
115331                     'turn_restriction_editing',
115332                     'route_h',
115333                     'route',
115334                     'route_add',
115335                     'boundary_h',
115336                     'boundary',
115337                     'boundary_add'
115338                 ]],
115339                 ['notes', [
115340                     'intro',
115341                     'add_note_h',
115342                     'add_note',
115343                     'place_note',
115344                     'move_note',
115345                     'update_note_h',
115346                     'update_note',
115347                     'save_note_h',
115348                     'save_note'
115349                 ]],
115350                 ['imagery', [
115351                     'intro',
115352                     'sources_h',
115353                     'choosing',
115354                     'sources',
115355                     'offsets_h',
115356                     'offset',
115357                     'offset_change'
115358                 ]],
115359                 ['streetlevel', [
115360                     'intro',
115361                     'using_h',
115362                     'using',
115363                     'photos',
115364                     'viewer'
115365                 ]],
115366                 ['gps', [
115367                     'intro',
115368                     'survey',
115369                     'using_h',
115370                     'using',
115371                     'tracing',
115372                     'upload'
115373                 ]],
115374                 ['qa', [
115375                     'intro',
115376                     'tools_h',
115377                     'tools',
115378                     'issues_h',
115379                     'issues'
115380                 ]]
115381             ];
115382
115383             var headings = {
115384                 'help.help.open_data_h': 3,
115385                 'help.help.before_start_h': 3,
115386                 'help.help.open_source_h': 3,
115387                 'help.overview.navigation_h': 3,
115388                 'help.overview.features_h': 3,
115389                 'help.editing.select_h': 3,
115390                 'help.editing.multiselect_h': 3,
115391                 'help.editing.undo_redo_h': 3,
115392                 'help.editing.save_h': 3,
115393                 'help.editing.upload_h': 3,
115394                 'help.editing.backups_h': 3,
115395                 'help.editing.keyboard_h': 3,
115396                 'help.feature_editor.type_h': 3,
115397                 'help.feature_editor.fields_h': 3,
115398                 'help.feature_editor.tags_h': 3,
115399                 'help.points.add_point_h': 3,
115400                 'help.points.move_point_h': 3,
115401                 'help.points.delete_point_h': 3,
115402                 'help.lines.add_line_h': 3,
115403                 'help.lines.modify_line_h': 3,
115404                 'help.lines.connect_line_h': 3,
115405                 'help.lines.disconnect_line_h': 3,
115406                 'help.lines.move_line_h': 3,
115407                 'help.lines.delete_line_h': 3,
115408                 'help.areas.point_or_area_h': 3,
115409                 'help.areas.add_area_h': 3,
115410                 'help.areas.square_area_h': 3,
115411                 'help.areas.modify_area_h': 3,
115412                 'help.areas.delete_area_h': 3,
115413                 'help.relations.edit_relation_h': 3,
115414                 'help.relations.maintain_relation_h': 3,
115415                 'help.relations.relation_types_h': 2,
115416                 'help.relations.multipolygon_h': 3,
115417                 'help.relations.turn_restriction_h': 3,
115418                 'help.relations.route_h': 3,
115419                 'help.relations.boundary_h': 3,
115420                 'help.notes.add_note_h': 3,
115421                 'help.notes.update_note_h': 3,
115422                 'help.notes.save_note_h': 3,
115423                 'help.imagery.sources_h': 3,
115424                 'help.imagery.offsets_h': 3,
115425                 'help.streetlevel.using_h': 3,
115426                 'help.gps.using_h': 3,
115427                 'help.qa.tools_h': 3,
115428                 'help.qa.issues_h': 3
115429             };
115430
115431             // For each section, squash all the texts into a single markdown document
115432             var docs = docKeys.map(function(key) {
115433                 var helpkey = 'help.' + key[0];
115434                 var helpPaneReplacements = { version: context.version };
115435                 var text = key[1].reduce(function(all, part) {
115436                     var subkey = helpkey + '.' + part;
115437                     var depth = headings[subkey];                              // is this subkey a heading?
115438                     var hhh = depth ? Array(depth + 1).join('#') + ' ' : '';   // if so, prepend with some ##'s
115439                     return all + hhh + helpString(subkey, helpPaneReplacements) + '\n\n';
115440                 }, '');
115441
115442                 return {
115443                     title: _t(helpkey + '.title'),
115444                     html: marked_1(text.trim())
115445                         // use keyboard key styling for shortcuts
115446                         .replace(/<code>/g, '<kbd>')
115447                         .replace(/<\/code>/g, '<\/kbd>')
115448                 };
115449             });
115450
115451             var helpPane = uiPane('help', context)
115452                 .key(_t('help.key'))
115453                 .title(_t('help.title'))
115454                 .description(_t('help.title'))
115455                 .iconName('iD-icon-help');
115456
115457             helpPane.renderContent = function(content) {
115458
115459                 function clickHelp(d, i) {
115460                     var rtl = (_mainLocalizer.textDirection() === 'rtl');
115461                     content.property('scrollTop', 0);
115462                     helpPane.selection().select('.pane-heading h2').html(d.title);
115463
115464                     body.html(d.html);
115465                     body.selectAll('a')
115466                         .attr('target', '_blank');
115467                     menuItems.classed('selected', function(m) {
115468                         return m.title === d.title;
115469                     });
115470
115471                     nav.html('');
115472                     if (rtl) {
115473                         nav.call(drawNext).call(drawPrevious);
115474                     } else {
115475                         nav.call(drawPrevious).call(drawNext);
115476                     }
115477
115478
115479                     function drawNext(selection) {
115480                         if (i < docs.length - 1) {
115481                             var nextLink = selection
115482                                 .append('a')
115483                                 .attr('class', 'next')
115484                                 .on('click', function() {
115485                                     clickHelp(docs[i + 1], i + 1);
115486                                 });
115487
115488                             nextLink
115489                                 .append('span')
115490                                 .text(docs[i + 1].title)
115491                                 .call(svgIcon((rtl ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'));
115492                         }
115493                     }
115494
115495
115496                     function drawPrevious(selection) {
115497                         if (i > 0) {
115498                             var prevLink = selection
115499                                 .append('a')
115500                                 .attr('class', 'previous')
115501                                 .on('click', function() {
115502                                     clickHelp(docs[i - 1], i - 1);
115503                                 });
115504
115505                             prevLink
115506                                 .call(svgIcon((rtl ? '#iD-icon-forward' : '#iD-icon-backward'), 'inline'))
115507                                 .append('span')
115508                                 .text(docs[i - 1].title);
115509                         }
115510                     }
115511                 }
115512
115513
115514                 function clickWalkthrough() {
115515                     if (context.inIntro()) return;
115516                     context.container().call(uiIntro(context));
115517                     context.ui().togglePanes();
115518                 }
115519
115520
115521                 function clickShortcuts() {
115522                     context.container().call(uiShortcuts(context), true);
115523                 }
115524
115525                 var toc = content
115526                     .append('ul')
115527                     .attr('class', 'toc');
115528
115529                 var menuItems = toc.selectAll('li')
115530                     .data(docs)
115531                     .enter()
115532                     .append('li')
115533                     .append('a')
115534                     .html(function(d) { return d.title; })
115535                     .on('click', clickHelp);
115536
115537                 var shortcuts = toc
115538                     .append('li')
115539                     .attr('class', 'shortcuts')
115540                     .call(uiTooltip()
115541                         .title(_t('shortcuts.tooltip'))
115542                         .keys(['?'])
115543                         .placement('top')
115544                     )
115545                     .append('a')
115546                     .on('click', clickShortcuts);
115547
115548                 shortcuts
115549                     .append('div')
115550                     .text(_t('shortcuts.title'));
115551
115552                 var walkthrough = toc
115553                     .append('li')
115554                     .attr('class', 'walkthrough')
115555                     .append('a')
115556                     .on('click', clickWalkthrough);
115557
115558                 walkthrough
115559                     .append('svg')
115560                     .attr('class', 'logo logo-walkthrough')
115561                     .append('use')
115562                     .attr('xlink:href', '#iD-logo-walkthrough');
115563
115564                 walkthrough
115565                     .append('div')
115566                     .text(_t('splash.walkthrough'));
115567
115568
115569                 var helpContent = content
115570                     .append('div')
115571                     .attr('class', 'left-content');
115572
115573                 var body = helpContent
115574                     .append('div')
115575                     .attr('class', 'body');
115576
115577                 var nav = helpContent
115578                     .append('div')
115579                     .attr('class', 'nav');
115580
115581                 clickHelp(docs[0], 0);
115582
115583             };
115584
115585             return helpPane;
115586         }
115587
115588         function uiSectionValidationIssues(id, severity, context) {
115589
115590             var _issues = [];
115591
115592             var section = uiSection(id, context)
115593                 .title(function() {
115594                     if (!_issues) return '';
115595                     var issueCountText = _issues.length > 1000 ? '1000+' : String(_issues.length);
115596                     return _t('issues.' + severity + 's.list_title', { count: issueCountText });
115597                 })
115598                 .disclosureContent(renderDisclosureContent)
115599                 .shouldDisplay(function() {
115600                     return _issues && _issues.length;
115601                 });
115602
115603             function getOptions() {
115604                 return {
115605                     what: corePreferences('validate-what') || 'edited',
115606                     where: corePreferences('validate-where') || 'all'
115607                 };
115608             }
115609
115610             // get and cache the issues to display, unordered
115611             function reloadIssues() {
115612                 _issues = context.validator().getIssuesBySeverity(getOptions())[severity];
115613             }
115614
115615             function renderDisclosureContent(selection) {
115616
115617                 var center = context.map().center();
115618                 var graph = context.graph();
115619
115620                 // sort issues by distance away from the center of the map
115621                 var issues = _issues.map(function withDistance(issue) {
115622                         var extent = issue.extent(graph);
115623                         var dist = extent ? geoSphericalDistance(center, extent.center()) : 0;
115624                         return Object.assign(issue, { dist: dist });
115625                     })
115626                     .sort(function byDistance(a, b) {
115627                         return a.dist - b.dist;
115628                     });
115629
115630                 // cut off at 1000
115631                 issues = issues.slice(0, 1000);
115632
115633                 //renderIgnoredIssuesReset(_warningsSelection);
115634
115635                 selection
115636                     .call(drawIssuesList, issues);
115637             }
115638
115639             function drawIssuesList(selection, issues) {
115640                 var list = selection.selectAll('.issues-list')
115641                     .data([0]);
115642
115643                 list = list.enter()
115644                     .append('ul')
115645                     .attr('class', 'layer-list issues-list ' + severity + 's-list')
115646                     .merge(list);
115647
115648
115649                 var items = list.selectAll('li')
115650                     .data(issues, function(d) { return d.id; });
115651
115652                 // Exit
115653                 items.exit()
115654                     .remove();
115655
115656                 // Enter
115657                 var itemsEnter = items.enter()
115658                     .append('li')
115659                     .attr('class', function (d) { return 'issue severity-' + d.severity; })
115660                     .on('click', function(d) {
115661                         context.validator().focusIssue(d);
115662                     })
115663                     .on('mouseover', function(d) {
115664                         utilHighlightEntities(d.entityIds, true, context);
115665                     })
115666                     .on('mouseout', function(d) {
115667                         utilHighlightEntities(d.entityIds, false, context);
115668                     });
115669
115670
115671                 var labelsEnter = itemsEnter
115672                     .append('div')
115673                     .attr('class', 'issue-label');
115674
115675                 var textEnter = labelsEnter
115676                     .append('span')
115677                     .attr('class', 'issue-text');
115678
115679                 textEnter
115680                     .append('span')
115681                     .attr('class', 'issue-icon')
115682                     .each(function(d) {
115683                         var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
115684                         select(this)
115685                             .call(svgIcon(iconName));
115686                     });
115687
115688                 textEnter
115689                     .append('span')
115690                     .attr('class', 'issue-message');
115691
115692                 /*
115693                 labelsEnter
115694                     .append('span')
115695                     .attr('class', 'issue-autofix')
115696                     .each(function(d) {
115697                         if (!d.autoFix) return;
115698
115699                         d3_select(this)
115700                             .append('button')
115701                             .attr('title', t('issues.fix_one.title'))
115702                             .datum(d.autoFix)  // set button datum to the autofix
115703                             .attr('class', 'autofix action')
115704                             .on('click', function(d) {
115705                                 d3_event.preventDefault();
115706                                 d3_event.stopPropagation();
115707
115708                                 var issuesEntityIDs = d.issue.entityIds;
115709                                 utilHighlightEntities(issuesEntityIDs.concat(d.entityIds), false, context);
115710
115711                                 context.perform.apply(context, d.autoArgs);
115712                                 context.validator().validate();
115713                             })
115714                             .call(svgIcon('#iD-icon-wrench'));
115715                     });
115716                 */
115717
115718                 // Update
115719                 items = items
115720                     .merge(itemsEnter)
115721                     .order();
115722
115723                 items.selectAll('.issue-message')
115724                     .text(function(d) {
115725                         return d.message(context);
115726                     });
115727
115728                 /*
115729                 // autofix
115730                 var canAutoFix = issues.filter(function(issue) { return issue.autoFix; });
115731
115732                 var autoFixAll = selection.selectAll('.autofix-all')
115733                     .data(canAutoFix.length ? [0] : []);
115734
115735                 // exit
115736                 autoFixAll.exit()
115737                     .remove();
115738
115739                 // enter
115740                 var autoFixAllEnter = autoFixAll.enter()
115741                     .insert('div', '.issues-list')
115742                     .attr('class', 'autofix-all');
115743
115744                 var linkEnter = autoFixAllEnter
115745                     .append('a')
115746                     .attr('class', 'autofix-all-link')
115747                     .attr('href', '#');
115748
115749                 linkEnter
115750                     .append('span')
115751                     .attr('class', 'autofix-all-link-text')
115752                     .text(t('issues.fix_all.title'));
115753
115754                 linkEnter
115755                     .append('span')
115756                     .attr('class', 'autofix-all-link-icon')
115757                     .call(svgIcon('#iD-icon-wrench'));
115758
115759                 if (severity === 'warning') {
115760                     renderIgnoredIssuesReset(selection);
115761                 }
115762
115763                 // update
115764                 autoFixAll = autoFixAll
115765                     .merge(autoFixAllEnter);
115766
115767                 autoFixAll.selectAll('.autofix-all-link')
115768                     .on('click', function() {
115769                         context.pauseChangeDispatch();
115770                         context.perform(actionNoop());
115771                         canAutoFix.forEach(function(issue) {
115772                             var args = issue.autoFix.autoArgs.slice();  // copy
115773                             if (typeof args[args.length - 1] !== 'function') {
115774                                 args.pop();
115775                             }
115776                             args.push(t('issues.fix_all.annotation'));
115777                             context.replace.apply(context, args);
115778                         });
115779                         context.resumeChangeDispatch();
115780                         context.validator().validate();
115781                     });
115782                 */
115783             }
115784
115785             context.validator().on('validated.uiSectionValidationIssues' + id, function() {
115786                 window.requestIdleCallback(function() {
115787                     reloadIssues();
115788                     section.reRender();
115789                 });
115790             });
115791
115792             context.map().on('move.uiSectionValidationIssues' + id,
115793                 debounce(function() {
115794                     window.requestIdleCallback(function() {
115795                         if (getOptions().where === 'visible') {
115796                             // must refetch issues if they are viewport-dependent
115797                             reloadIssues();
115798                         }
115799                         // always reload list to re-sort-by-distance
115800                         section.reRender();
115801                     });
115802                 }, 1000)
115803             );
115804
115805             return section;
115806         }
115807
115808         function uiSectionValidationOptions(context) {
115809
115810             var section = uiSection('issues-options', context)
115811                 .content(renderContent);
115812
115813             function renderContent(selection) {
115814
115815                 var container = selection.selectAll('.issues-options-container')
115816                     .data([0]);
115817
115818                 container = container.enter()
115819                     .append('div')
115820                     .attr('class', 'issues-options-container')
115821                     .merge(container);
115822
115823                 var data = [
115824                     { key: 'what', values: ['edited', 'all'] },
115825                     { key: 'where', values: ['visible', 'all'] }
115826                 ];
115827
115828                 var options = container.selectAll('.issues-option')
115829                     .data(data, function(d) { return d.key; });
115830
115831                 var optionsEnter = options.enter()
115832                     .append('div')
115833                     .attr('class', function(d) { return 'issues-option issues-option-' + d.key; });
115834
115835                 optionsEnter
115836                     .append('div')
115837                     .attr('class', 'issues-option-title')
115838                     .text(function(d) { return _t('issues.options.' + d.key + '.title'); });
115839
115840                 var valuesEnter = optionsEnter.selectAll('label')
115841                     .data(function(d) {
115842                         return d.values.map(function(val) { return { value: val, key: d.key }; });
115843                     })
115844                     .enter()
115845                     .append('label');
115846
115847                 valuesEnter
115848                     .append('input')
115849                     .attr('type', 'radio')
115850                     .attr('name', function(d) { return 'issues-option-' + d.key; })
115851                     .attr('value', function(d) { return d.value; })
115852                     .property('checked', function(d) { return getOptions()[d.key] === d.value; })
115853                     .on('change', function(d) { updateOptionValue(d.key, d.value); });
115854
115855                 valuesEnter
115856                     .append('span')
115857                     .text(function(d) { return _t('issues.options.' + d.key + '.' + d.value); });
115858             }
115859
115860             function getOptions() {
115861                 return {
115862                     what: corePreferences('validate-what') || 'edited',  // 'all', 'edited'
115863                     where: corePreferences('validate-where') || 'all'    // 'all', 'visible'
115864                 };
115865             }
115866
115867             function updateOptionValue(d, val) {
115868                 if (!val && event && event.target) {
115869                     val = event.target.value;
115870                 }
115871
115872                 corePreferences('validate-' + d, val);
115873                 context.validator().validate();
115874             }
115875
115876             return section;
115877         }
115878
115879         function uiSectionValidationRules(context) {
115880
115881             var MINSQUARE = 0;
115882             var MAXSQUARE = 20;
115883             var DEFAULTSQUARE = 5;  // see also unsquare_way.js
115884
115885             var section = uiSection('issues-rules', context)
115886                 .disclosureContent(renderDisclosureContent)
115887                 .title(_t('issues.rules.title'));
115888
115889             var _ruleKeys = context.validator().getRuleKeys()
115890                 .filter(function(key) { return key !== 'maprules'; })
115891                 .sort(function(key1, key2) {
115892                     // alphabetize by localized title
115893                     return _t('issues.' + key1 + '.title') < _t('issues.' + key2 + '.title') ? -1 : 1;
115894                 });
115895
115896             function renderDisclosureContent(selection) {
115897                 var container = selection.selectAll('.issues-rulelist-container')
115898                     .data([0]);
115899
115900                 var containerEnter = container.enter()
115901                     .append('div')
115902                     .attr('class', 'issues-rulelist-container');
115903
115904                 containerEnter
115905                     .append('ul')
115906                     .attr('class', 'layer-list issue-rules-list');
115907
115908                 var ruleLinks = containerEnter
115909                     .append('div')
115910                     .attr('class', 'issue-rules-links section-footer');
115911
115912                 ruleLinks
115913                     .append('a')
115914                     .attr('class', 'issue-rules-link')
115915                     .attr('href', '#')
115916                     .text(_t('issues.enable_all'))
115917                     .on('click', function() {
115918                         context.validator().disableRules([]);
115919                     });
115920
115921                 ruleLinks
115922                     .append('a')
115923                     .attr('class', 'issue-rules-link')
115924                     .attr('href', '#')
115925                     .text(_t('issues.disable_all'))
115926                     .on('click', function() {
115927                         context.validator().disableRules(_ruleKeys);
115928                     });
115929
115930
115931                 // Update
115932                 container = container
115933                     .merge(containerEnter);
115934
115935                 container.selectAll('.issue-rules-list')
115936                     .call(drawListItems, _ruleKeys, 'checkbox', 'rule', toggleRule, isRuleEnabled);
115937             }
115938
115939             function drawListItems(selection, data, type, name, change, active) {
115940                 var items = selection.selectAll('li')
115941                     .data(data);
115942
115943                 // Exit
115944                 items.exit()
115945                     .remove();
115946
115947                 // Enter
115948                 var enter = items.enter()
115949                     .append('li');
115950
115951                 if (name === 'rule') {
115952                     enter
115953                         .call(uiTooltip()
115954                             .title(function(d) { return _t('issues.' + d + '.tip'); })
115955                             .placement('top')
115956                         );
115957                 }
115958
115959                 var label = enter
115960                     .append('label');
115961
115962                 label
115963                     .append('input')
115964                     .attr('type', type)
115965                     .attr('name', name)
115966                     .on('change', change);
115967
115968                 label
115969                     .append('span')
115970                     .html(function(d) {
115971                         var params = {};
115972                         if (d === 'unsquare_way') {
115973                             params.val = '<span class="square-degrees"></span>';
115974                         }
115975                         return _t('issues.' + d + '.title', params);
115976                     });
115977
115978                 // Update
115979                 items = items
115980                     .merge(enter);
115981
115982                 items
115983                     .classed('active', active)
115984                     .selectAll('input')
115985                     .property('checked', active)
115986                     .property('indeterminate', false);
115987
115988
115989                 // user-configurable square threshold
115990                 var degStr = corePreferences('validate-square-degrees');
115991                 if (degStr === null) {
115992                     degStr = '' + DEFAULTSQUARE;
115993                 }
115994
115995                 var span = items.selectAll('.square-degrees');
115996                 var input = span.selectAll('.square-degrees-input')
115997                     .data([0]);
115998
115999                 // enter / update
116000                 input.enter()
116001                     .append('input')
116002                     .attr('type', 'number')
116003                     .attr('min', '' + MINSQUARE)
116004                     .attr('max', '' + MAXSQUARE)
116005                     .attr('step', '0.5')
116006                     .attr('class', 'square-degrees-input')
116007                     .call(utilNoAuto)
116008                     .on('click', function () {
116009                         event.preventDefault();
116010                         event.stopPropagation();
116011                         this.select();
116012                     })
116013                     .on('keyup', function () {
116014                         if (event.keyCode === 13) { // enter
116015                             this.blur();
116016                             this.select();
116017                         }
116018                     })
116019                     .on('blur', changeSquare)
116020                     .merge(input)
116021                     .property('value', degStr);
116022             }
116023
116024             function changeSquare() {
116025                 var input = select(this);
116026                 var degStr = utilGetSetValue(input).trim();
116027                 var degNum = parseFloat(degStr, 10);
116028
116029                 if (!isFinite(degNum)) {
116030                     degNum = DEFAULTSQUARE;
116031                 } else if (degNum > MAXSQUARE) {
116032                     degNum = MAXSQUARE;
116033                 } else if (degNum < MINSQUARE) {
116034                     degNum = MINSQUARE;
116035                 }
116036
116037                 degNum = Math.round(degNum * 10 ) / 10;   // round to 1 decimal
116038                 degStr = '' + degNum;
116039
116040                 input
116041                     .property('value', degStr);
116042
116043                 corePreferences('validate-square-degrees', degStr);
116044                 context.validator().reloadUnsquareIssues();
116045             }
116046
116047             function isRuleEnabled(d) {
116048                 return context.validator().isRuleEnabled(d);
116049             }
116050
116051             function toggleRule(d) {
116052                 context.validator().toggleRule(d);
116053             }
116054
116055             context.validator().on('validated.uiSectionValidationRules', function() {
116056                 window.requestIdleCallback(section.reRender);
116057             });
116058
116059             return section;
116060         }
116061
116062         function uiSectionValidationStatus(context) {
116063
116064             var section = uiSection('issues-status', context)
116065                 .content(renderContent)
116066                 .shouldDisplay(function() {
116067                     var issues = context.validator().getIssues(getOptions());
116068                     return issues.length === 0;
116069                 });
116070
116071             function getOptions() {
116072                 return {
116073                     what: corePreferences('validate-what') || 'edited',
116074                     where: corePreferences('validate-where') || 'all'
116075                 };
116076             }
116077
116078             function renderContent(selection) {
116079
116080                 var box = selection.selectAll('.box')
116081                     .data([0]);
116082
116083                 var boxEnter = box.enter()
116084                     .append('div')
116085                     .attr('class', 'box');
116086
116087                 boxEnter
116088                     .append('div')
116089                     .call(svgIcon('#iD-icon-apply', 'pre-text'));
116090
116091                 var noIssuesMessage = boxEnter
116092                     .append('span');
116093
116094                 noIssuesMessage
116095                     .append('strong')
116096                     .attr('class', 'message');
116097
116098                 noIssuesMessage
116099                     .append('br');
116100
116101                 noIssuesMessage
116102                     .append('span')
116103                     .attr('class', 'details');
116104
116105                 renderIgnoredIssuesReset(selection);
116106                 setNoIssuesText(selection);
116107             }
116108
116109             function renderIgnoredIssuesReset(selection) {
116110
116111                 var ignoredIssues = context.validator()
116112                     .getIssues({ what: 'all', where: 'all', includeDisabledRules: true, includeIgnored: 'only' });
116113
116114                 var resetIgnored = selection.selectAll('.reset-ignored')
116115                     .data(ignoredIssues.length ? [0] : []);
116116
116117                 // exit
116118                 resetIgnored.exit()
116119                     .remove();
116120
116121                 // enter
116122                 var resetIgnoredEnter = resetIgnored.enter()
116123                     .append('div')
116124                     .attr('class', 'reset-ignored section-footer');
116125
116126                 resetIgnoredEnter
116127                     .append('a')
116128                     .attr('href', '#');
116129
116130                 // update
116131                 resetIgnored = resetIgnored
116132                     .merge(resetIgnoredEnter);
116133
116134                 resetIgnored.select('a')
116135                     .text(_t('issues.reset_ignored', { count: ignoredIssues.length.toString() }));
116136
116137                 resetIgnored.on('click', function() {
116138                     context.validator().resetIgnoredIssues();
116139                 });
116140             }
116141
116142             function setNoIssuesText(selection) {
116143
116144                 var opts = getOptions();
116145
116146                 function checkForHiddenIssues(cases) {
116147                     for (var type in cases) {
116148                         var hiddenOpts = cases[type];
116149                         var hiddenIssues = context.validator().getIssues(hiddenOpts);
116150                         if (hiddenIssues.length) {
116151                             selection.select('.box .details')
116152                                 .text(_t(
116153                                     'issues.no_issues.hidden_issues.' + type,
116154                                     { count: hiddenIssues.length.toString() }
116155                                 ));
116156                             return;
116157                         }
116158                     }
116159                     selection.select('.box .details')
116160                         .text(_t('issues.no_issues.hidden_issues.none'));
116161                 }
116162
116163                 var messageType;
116164
116165                 if (opts.what === 'edited' && opts.where === 'visible') {
116166
116167                     messageType = 'edits_in_view';
116168
116169                     checkForHiddenIssues({
116170                         elsewhere: { what: 'edited', where: 'all' },
116171                         everything_else: { what: 'all', where: 'visible' },
116172                         disabled_rules: { what: 'edited', where: 'visible', includeDisabledRules: 'only' },
116173                         everything_else_elsewhere: { what: 'all', where: 'all' },
116174                         disabled_rules_elsewhere: { what: 'edited', where: 'all', includeDisabledRules: 'only' },
116175                         ignored_issues: { what: 'edited', where: 'visible', includeIgnored: 'only' },
116176                         ignored_issues_elsewhere: { what: 'edited', where: 'all', includeIgnored: 'only' }
116177                     });
116178
116179                 } else if (opts.what === 'edited' && opts.where === 'all') {
116180
116181                     messageType = 'edits';
116182
116183                     checkForHiddenIssues({
116184                         everything_else: { what: 'all', where: 'all' },
116185                         disabled_rules: { what: 'edited', where: 'all', includeDisabledRules: 'only' },
116186                         ignored_issues: { what: 'edited', where: 'all', includeIgnored: 'only' }
116187                     });
116188
116189                 } else if (opts.what === 'all' && opts.where === 'visible') {
116190
116191                     messageType = 'everything_in_view';
116192
116193                     checkForHiddenIssues({
116194                         elsewhere: { what: 'all', where: 'all' },
116195                         disabled_rules: { what: 'all', where: 'visible', includeDisabledRules: 'only' },
116196                         disabled_rules_elsewhere: { what: 'all', where: 'all', includeDisabledRules: 'only' },
116197                         ignored_issues: { what: 'all', where: 'visible', includeIgnored: 'only' },
116198                         ignored_issues_elsewhere: { what: 'all', where: 'all', includeIgnored: 'only' }
116199                     });
116200                 } else if (opts.what === 'all' && opts.where === 'all') {
116201
116202                     messageType = 'everything';
116203
116204                     checkForHiddenIssues({
116205                         disabled_rules: { what: 'all', where: 'all', includeDisabledRules: 'only' },
116206                         ignored_issues: { what: 'all', where: 'all', includeIgnored: 'only' }
116207                     });
116208                 }
116209
116210                 if (opts.what === 'edited' && context.history().difference().summary().length === 0) {
116211                     messageType = 'no_edits';
116212                 }
116213
116214                 selection.select('.box .message')
116215                     .text(_t('issues.no_issues.message.' + messageType));
116216
116217             }
116218
116219             context.validator().on('validated.uiSectionValidationStatus', function() {
116220                 window.requestIdleCallback(section.reRender);
116221             });
116222
116223             context.map().on('move.uiSectionValidationStatus',
116224                 debounce(function() {
116225                     window.requestIdleCallback(section.reRender);
116226                 }, 1000)
116227             );
116228
116229             return section;
116230         }
116231
116232         function uiPaneIssues(context) {
116233
116234             var issuesPane = uiPane('issues', context)
116235                 .key(_t('issues.key'))
116236                 .title(_t('issues.title'))
116237                 .description(_t('issues.title'))
116238                 .iconName('iD-icon-alert')
116239                 .sections([
116240                     uiSectionValidationOptions(context),
116241                     uiSectionValidationStatus(context),
116242                     uiSectionValidationIssues('issues-errors', 'error', context),
116243                     uiSectionValidationIssues('issues-warnings', 'warning', context),
116244                     uiSectionValidationRules(context)
116245                 ]);
116246
116247             return issuesPane;
116248         }
116249
116250         function uiSettingsCustomData(context) {
116251             var dispatch$1 = dispatch('change');
116252
116253             function render(selection) {
116254                 var dataLayer = context.layers().layer('data');
116255
116256                 // keep separate copies of original and current settings
116257                 var _origSettings = {
116258                     fileList: (dataLayer && dataLayer.fileList()) || null,
116259                     url: corePreferences('settings-custom-data-url')
116260                 };
116261                 var _currSettings = {
116262                     fileList: (dataLayer && dataLayer.fileList()) || null,
116263                     url: corePreferences('settings-custom-data-url')
116264                 };
116265
116266                 // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
116267                 var modal = uiConfirm(selection).okButton();
116268
116269                 modal
116270                     .classed('settings-modal settings-custom-data', true);
116271
116272                 modal.select('.modal-section.header')
116273                     .append('h3')
116274                     .text(_t('settings.custom_data.header'));
116275
116276
116277                 var textSection = modal.select('.modal-section.message-text');
116278
116279                 textSection
116280                     .append('pre')
116281                     .attr('class', 'instructions-file')
116282                     .text(_t('settings.custom_data.file.instructions'));
116283
116284                 textSection
116285                     .append('input')
116286                     .attr('class', 'field-file')
116287                     .attr('type', 'file')
116288                     .property('files', _currSettings.fileList)  // works for all except IE11
116289                     .on('change', function() {
116290                         var files = event.target.files;
116291                         if (files && files.length) {
116292                             _currSettings.url = '';
116293                             textSection.select('.field-url').property('value', '');
116294                             _currSettings.fileList = files;
116295                         } else {
116296                             _currSettings.fileList = null;
116297                         }
116298                     });
116299
116300                 textSection
116301                     .append('h4')
116302                     .text(_t('settings.custom_data.or'));
116303
116304                 textSection
116305                     .append('pre')
116306                     .attr('class', 'instructions-url')
116307                     .text(_t('settings.custom_data.url.instructions'));
116308
116309                 textSection
116310                     .append('textarea')
116311                     .attr('class', 'field-url')
116312                     .attr('placeholder', _t('settings.custom_data.url.placeholder'))
116313                     .call(utilNoAuto)
116314                     .property('value', _currSettings.url);
116315
116316
116317                 // insert a cancel button
116318                 var buttonSection = modal.select('.modal-section.buttons');
116319
116320                 buttonSection
116321                     .insert('button', '.ok-button')
116322                     .attr('class', 'button cancel-button secondary-action')
116323                     .text(_t('confirm.cancel'));
116324
116325
116326                 buttonSection.select('.cancel-button')
116327                     .on('click.cancel', clickCancel);
116328
116329                 buttonSection.select('.ok-button')
116330                     .attr('disabled', isSaveDisabled)
116331                     .on('click.save', clickSave);
116332
116333
116334                 function isSaveDisabled() {
116335                     return null;
116336                 }
116337
116338
116339                 // restore the original url
116340                 function clickCancel() {
116341                     textSection.select('.field-url').property('value', _origSettings.url);
116342                     corePreferences('settings-custom-data-url', _origSettings.url);
116343                     this.blur();
116344                     modal.close();
116345                 }
116346
116347                 // accept the current url
116348                 function clickSave() {
116349                     _currSettings.url = textSection.select('.field-url').property('value').trim();
116350
116351                     // one or the other but not both
116352                     if (_currSettings.url) { _currSettings.fileList = null; }
116353                     if (_currSettings.fileList) { _currSettings.url = ''; }
116354
116355                     corePreferences('settings-custom-data-url', _currSettings.url);
116356                     this.blur();
116357                     modal.close();
116358                     dispatch$1.call('change', this, _currSettings);
116359                 }
116360             }
116361
116362             return utilRebind(render, dispatch$1, 'on');
116363         }
116364
116365         function uiSectionDataLayers(context) {
116366
116367             var settingsCustomData = uiSettingsCustomData(context)
116368                 .on('change', customChanged);
116369
116370             var layers = context.layers();
116371
116372             var section = uiSection('data-layers', context)
116373                 .title(_t('map_data.data_layers'))
116374                 .disclosureContent(renderDisclosureContent);
116375
116376             function renderDisclosureContent(selection) {
116377                 var container = selection.selectAll('.data-layer-container')
116378                     .data([0]);
116379
116380                 container.enter()
116381                     .append('div')
116382                     .attr('class', 'data-layer-container')
116383                     .merge(container)
116384                     .call(drawOsmItems)
116385                     .call(drawQAItems)
116386                     .call(drawCustomDataItems)
116387                     .call(drawVectorItems)      // Beta - Detroit mapping challenge
116388                     .call(drawPanelItems);
116389             }
116390
116391             function showsLayer(which) {
116392                 var layer = layers.layer(which);
116393                 if (layer) {
116394                     return layer.enabled();
116395                 }
116396                 return false;
116397             }
116398
116399             function setLayer(which, enabled) {
116400                 // Don't allow layer changes while drawing - #6584
116401                 var mode = context.mode();
116402                 if (mode && /^draw/.test(mode.id)) return;
116403
116404                 var layer = layers.layer(which);
116405                 if (layer) {
116406                     layer.enabled(enabled);
116407
116408                     if (!enabled && (which === 'osm' || which === 'notes')) {
116409                         context.enter(modeBrowse(context));
116410                     }
116411                 }
116412             }
116413
116414             function toggleLayer(which) {
116415                 setLayer(which, !showsLayer(which));
116416             }
116417
116418             function drawOsmItems(selection) {
116419                 var osmKeys = ['osm', 'notes'];
116420                 var osmLayers = layers.all().filter(function(obj) { return osmKeys.indexOf(obj.id) !== -1; });
116421
116422                 var ul = selection
116423                     .selectAll('.layer-list-osm')
116424                     .data([0]);
116425
116426                 ul = ul.enter()
116427                     .append('ul')
116428                     .attr('class', 'layer-list layer-list-osm')
116429                     .merge(ul);
116430
116431                 var li = ul.selectAll('.list-item')
116432                     .data(osmLayers);
116433
116434                 li.exit()
116435                     .remove();
116436
116437                 var liEnter = li.enter()
116438                     .append('li')
116439                     .attr('class', function(d) { return 'list-item list-item-' + d.id; });
116440
116441                 var labelEnter = liEnter
116442                     .append('label')
116443                     .each(function(d) {
116444                         if (d.id === 'osm') {
116445                             select(this)
116446                                 .call(uiTooltip()
116447                                     .title(_t('map_data.layers.' + d.id + '.tooltip'))
116448                                     .keys([uiCmd('⌥' + _t('area_fill.wireframe.key'))])
116449                                     .placement('bottom')
116450                                 );
116451                         } else {
116452                             select(this)
116453                                 .call(uiTooltip()
116454                                     .title(_t('map_data.layers.' + d.id + '.tooltip'))
116455                                     .placement('bottom')
116456                                 );
116457                         }
116458                     });
116459
116460                 labelEnter
116461                     .append('input')
116462                     .attr('type', 'checkbox')
116463                     .on('change', function(d) { toggleLayer(d.id); });
116464
116465                 labelEnter
116466                     .append('span')
116467                     .text(function(d) { return _t('map_data.layers.' + d.id + '.title'); });
116468
116469
116470                 // Update
116471                 li
116472                     .merge(liEnter)
116473                     .classed('active', function (d) { return d.layer.enabled(); })
116474                     .selectAll('input')
116475                     .property('checked', function (d) { return d.layer.enabled(); });
116476             }
116477
116478             function drawQAItems(selection) {
116479                 var qaKeys = ['keepRight', 'improveOSM', 'osmose'];
116480                 var qaLayers = layers.all().filter(function(obj) { return qaKeys.indexOf(obj.id) !== -1; });
116481
116482                 var ul = selection
116483                     .selectAll('.layer-list-qa')
116484                     .data([0]);
116485
116486                 ul = ul.enter()
116487                     .append('ul')
116488                     .attr('class', 'layer-list layer-list-qa')
116489                     .merge(ul);
116490
116491                 var li = ul.selectAll('.list-item')
116492                     .data(qaLayers);
116493
116494                 li.exit()
116495                     .remove();
116496
116497                 var liEnter = li.enter()
116498                     .append('li')
116499                     .attr('class', function(d) { return 'list-item list-item-' + d.id; });
116500
116501                 var labelEnter = liEnter
116502                     .append('label')
116503                     .each(function(d) {
116504                         select(this)
116505                             .call(uiTooltip()
116506                                 .title(_t('map_data.layers.' + d.id + '.tooltip'))
116507                                 .placement('bottom')
116508                             );
116509                     });
116510
116511                 labelEnter
116512                     .append('input')
116513                     .attr('type', 'checkbox')
116514                     .on('change', function(d) { toggleLayer(d.id); });
116515
116516                 labelEnter
116517                     .append('span')
116518                     .text(function(d) { return _t('map_data.layers.' + d.id + '.title'); });
116519
116520
116521                 // Update
116522                 li
116523                     .merge(liEnter)
116524                     .classed('active', function (d) { return d.layer.enabled(); })
116525                     .selectAll('input')
116526                     .property('checked', function (d) { return d.layer.enabled(); });
116527             }
116528
116529             // Beta feature - sample vector layers to support Detroit Mapping Challenge
116530             // https://github.com/osmus/detroit-mapping-challenge
116531             function drawVectorItems(selection) {
116532                 var dataLayer = layers.layer('data');
116533                 var vtData = [
116534                     {
116535                         name: 'Detroit Neighborhoods/Parks',
116536                         src: 'neighborhoods-parks',
116537                         tooltip: 'Neighborhood boundaries and parks as compiled by City of Detroit in concert with community groups.',
116538                         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'
116539                     }, {
116540                         name: 'Detroit Composite POIs',
116541                         src: 'composite-poi',
116542                         tooltip: 'Fire Inspections, Business Licenses, and other public location data collated from the City of Detroit.',
116543                         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'
116544                     }, {
116545                         name: 'Detroit All-The-Places POIs',
116546                         src: 'alltheplaces-poi',
116547                         tooltip: 'Public domain business location data created by web scrapers.',
116548                         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'
116549                     }
116550                 ];
116551
116552                 // Only show this if the map is around Detroit..
116553                 var detroit = geoExtent([-83.5, 42.1], [-82.8, 42.5]);
116554                 var showVectorItems = (context.map().zoom() > 9 && detroit.contains(context.map().center()));
116555
116556                 var container = selection.selectAll('.vectortile-container')
116557                     .data(showVectorItems ? [0] : []);
116558
116559                 container.exit()
116560                     .remove();
116561
116562                 var containerEnter = container.enter()
116563                     .append('div')
116564                     .attr('class', 'vectortile-container');
116565
116566                 containerEnter
116567                     .append('h4')
116568                     .attr('class', 'vectortile-header')
116569                     .text('Detroit Vector Tiles (Beta)');
116570
116571                 containerEnter
116572                     .append('ul')
116573                     .attr('class', 'layer-list layer-list-vectortile');
116574
116575                 containerEnter
116576                     .append('div')
116577                     .attr('class', 'vectortile-footer')
116578                     .append('a')
116579                     .attr('target', '_blank')
116580                     .attr('tabindex', -1)
116581                     .call(svgIcon('#iD-icon-out-link', 'inline'))
116582                     .attr('href', 'https://github.com/osmus/detroit-mapping-challenge')
116583                     .append('span')
116584                     .text('About these layers');
116585
116586                 container = container
116587                     .merge(containerEnter);
116588
116589
116590                 var ul = container.selectAll('.layer-list-vectortile');
116591
116592                 var li = ul.selectAll('.list-item')
116593                     .data(vtData);
116594
116595                 li.exit()
116596                     .remove();
116597
116598                 var liEnter = li.enter()
116599                     .append('li')
116600                     .attr('class', function(d) { return 'list-item list-item-' + d.src; });
116601
116602                 var labelEnter = liEnter
116603                     .append('label')
116604                     .each(function(d) {
116605                         select(this).call(
116606                             uiTooltip().title(d.tooltip).placement('top')
116607                         );
116608                     });
116609
116610                 labelEnter
116611                     .append('input')
116612                     .attr('type', 'radio')
116613                     .attr('name', 'vectortile')
116614                     .on('change', selectVTLayer);
116615
116616                 labelEnter
116617                     .append('span')
116618                     .text(function(d) { return d.name; });
116619
116620                 // Update
116621                 li
116622                     .merge(liEnter)
116623                     .classed('active', isVTLayerSelected)
116624                     .selectAll('input')
116625                     .property('checked', isVTLayerSelected);
116626
116627
116628                 function isVTLayerSelected(d) {
116629                     return dataLayer && dataLayer.template() === d.template;
116630                 }
116631
116632                 function selectVTLayer(d) {
116633                     corePreferences('settings-custom-data-url', d.template);
116634                     if (dataLayer) {
116635                         dataLayer.template(d.template, d.src);
116636                         dataLayer.enabled(true);
116637                     }
116638                 }
116639             }
116640
116641             function drawCustomDataItems(selection) {
116642                 var dataLayer = layers.layer('data');
116643                 var hasData = dataLayer && dataLayer.hasData();
116644                 var showsData = hasData && dataLayer.enabled();
116645
116646                 var ul = selection
116647                     .selectAll('.layer-list-data')
116648                     .data(dataLayer ? [0] : []);
116649
116650                 // Exit
116651                 ul.exit()
116652                     .remove();
116653
116654                 // Enter
116655                 var ulEnter = ul.enter()
116656                     .append('ul')
116657                     .attr('class', 'layer-list layer-list-data');
116658
116659                 var liEnter = ulEnter
116660                     .append('li')
116661                     .attr('class', 'list-item-data');
116662
116663                 var labelEnter = liEnter
116664                     .append('label')
116665                     .call(uiTooltip()
116666                         .title(_t('map_data.layers.custom.tooltip'))
116667                         .placement('top')
116668                     );
116669
116670                 labelEnter
116671                     .append('input')
116672                     .attr('type', 'checkbox')
116673                     .on('change', function() { toggleLayer('data'); });
116674
116675                 labelEnter
116676                     .append('span')
116677                     .text(_t('map_data.layers.custom.title'));
116678
116679                 liEnter
116680                     .append('button')
116681                     .call(uiTooltip()
116682                         .title(_t('settings.custom_data.tooltip'))
116683                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
116684                     )
116685                     .on('click', editCustom)
116686                     .call(svgIcon('#iD-icon-more'));
116687
116688                 liEnter
116689                     .append('button')
116690                     .call(uiTooltip()
116691                         .title(_t('map_data.layers.custom.zoom'))
116692                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
116693                     )
116694                     .on('click', function() {
116695                         event.preventDefault();
116696                         event.stopPropagation();
116697                         dataLayer.fitZoom();
116698                     })
116699                     .call(svgIcon('#iD-icon-framed-dot'));
116700
116701                 // Update
116702                 ul = ul
116703                     .merge(ulEnter);
116704
116705                 ul.selectAll('.list-item-data')
116706                     .classed('active', showsData)
116707                     .selectAll('label')
116708                     .classed('deemphasize', !hasData)
116709                     .selectAll('input')
116710                     .property('disabled', !hasData)
116711                     .property('checked', showsData);
116712             }
116713
116714             function editCustom() {
116715                 event.preventDefault();
116716                 context.container()
116717                     .call(settingsCustomData);
116718             }
116719
116720             function customChanged(d) {
116721                 var dataLayer = layers.layer('data');
116722
116723                 if (d && d.url) {
116724                     dataLayer.url(d.url);
116725                 } else if (d && d.fileList) {
116726                     dataLayer.fileList(d.fileList);
116727                 }
116728             }
116729
116730
116731             function drawPanelItems(selection) {
116732
116733                 var panelsListEnter = selection.selectAll('.md-extras-list')
116734                     .data([0])
116735                     .enter()
116736                     .append('ul')
116737                     .attr('class', 'layer-list md-extras-list');
116738
116739                 var historyPanelLabelEnter = panelsListEnter
116740                     .append('li')
116741                     .attr('class', 'history-panel-toggle-item')
116742                     .append('label')
116743                     .call(uiTooltip()
116744                         .title(_t('map_data.history_panel.tooltip'))
116745                         .keys([uiCmd('⌘⇧' + _t('info_panels.history.key'))])
116746                         .placement('top')
116747                     );
116748
116749                 historyPanelLabelEnter
116750                     .append('input')
116751                     .attr('type', 'checkbox')
116752                     .on('change', function() {
116753                         event.preventDefault();
116754                         context.ui().info.toggle('history');
116755                     });
116756
116757                 historyPanelLabelEnter
116758                     .append('span')
116759                     .text(_t('map_data.history_panel.title'));
116760
116761                 var measurementPanelLabelEnter = panelsListEnter
116762                     .append('li')
116763                     .attr('class', 'measurement-panel-toggle-item')
116764                     .append('label')
116765                     .call(uiTooltip()
116766                         .title(_t('map_data.measurement_panel.tooltip'))
116767                         .keys([uiCmd('⌘⇧' + _t('info_panels.measurement.key'))])
116768                         .placement('top')
116769                     );
116770
116771                 measurementPanelLabelEnter
116772                     .append('input')
116773                     .attr('type', 'checkbox')
116774                     .on('change', function() {
116775                         event.preventDefault();
116776                         context.ui().info.toggle('measurement');
116777                     });
116778
116779                 measurementPanelLabelEnter
116780                     .append('span')
116781                     .text(_t('map_data.measurement_panel.title'));
116782             }
116783
116784             context.layers().on('change.uiSectionDataLayers', section.reRender);
116785
116786             context.map()
116787                 .on('move.uiSectionDataLayers',
116788                     debounce(function() {
116789                         // Detroit layers may have moved in or out of view
116790                         window.requestIdleCallback(section.reRender);
116791                     }, 1000)
116792                 );
116793
116794             return section;
116795         }
116796
116797         function uiSectionMapFeatures(context) {
116798
116799             var _features = context.features().keys();
116800
116801             var section = uiSection('map-features', context)
116802                 .title(_t('map_data.map_features'))
116803                 .disclosureContent(renderDisclosureContent)
116804                 .expandedByDefault(false);
116805
116806             function renderDisclosureContent(selection) {
116807
116808                 var container = selection.selectAll('.layer-feature-list-container')
116809                     .data([0]);
116810
116811                 var containerEnter = container.enter()
116812                     .append('div')
116813                     .attr('class', 'layer-feature-list-container');
116814
116815                 containerEnter
116816                     .append('ul')
116817                     .attr('class', 'layer-list layer-feature-list');
116818
116819                 var footer = containerEnter
116820                     .append('div')
116821                     .attr('class', 'feature-list-links section-footer');
116822
116823                 footer
116824                     .append('a')
116825                     .attr('class', 'feature-list-link')
116826                     .attr('href', '#')
116827                     .text(_t('issues.enable_all'))
116828                     .on('click', function() {
116829                         context.features().enableAll();
116830                     });
116831
116832                 footer
116833                     .append('a')
116834                     .attr('class', 'feature-list-link')
116835                     .attr('href', '#')
116836                     .text(_t('issues.disable_all'))
116837                     .on('click', function() {
116838                         context.features().disableAll();
116839                     });
116840
116841                 // Update
116842                 container = container
116843                     .merge(containerEnter);
116844
116845                 container.selectAll('.layer-feature-list')
116846                     .call(drawListItems, _features, 'checkbox', 'feature', clickFeature, showsFeature);
116847             }
116848
116849             function drawListItems(selection, data, type, name, change, active) {
116850                 var items = selection.selectAll('li')
116851                     .data(data);
116852
116853                 // Exit
116854                 items.exit()
116855                     .remove();
116856
116857                 // Enter
116858                 var enter = items.enter()
116859                     .append('li')
116860                     .call(uiTooltip()
116861                         .title(function(d) {
116862                             var tip = _t(name + '.' + d + '.tooltip');
116863                             if (autoHiddenFeature(d)) {
116864                                 var msg = showsLayer('osm') ? _t('map_data.autohidden') : _t('map_data.osmhidden');
116865                                 tip += '<div>' + msg + '</div>';
116866                             }
116867                             return tip;
116868                         })
116869                         .placement('top')
116870                     );
116871
116872                 var label = enter
116873                     .append('label');
116874
116875                 label
116876                     .append('input')
116877                     .attr('type', type)
116878                     .attr('name', name)
116879                     .on('change', change);
116880
116881                 label
116882                     .append('span')
116883                     .text(function(d) { return _t(name + '.' + d + '.description'); });
116884
116885                 // Update
116886                 items = items
116887                     .merge(enter);
116888
116889                 items
116890                     .classed('active', active)
116891                     .selectAll('input')
116892                     .property('checked', active)
116893                     .property('indeterminate', autoHiddenFeature);
116894             }
116895
116896             function autoHiddenFeature(d) {
116897                 return context.features().autoHidden(d);
116898             }
116899
116900             function showsFeature(d) {
116901                 return context.features().enabled(d);
116902             }
116903
116904             function clickFeature(d) {
116905                 context.features().toggle(d);
116906             }
116907
116908             function showsLayer(id) {
116909                 var layer = context.layers().layer(id);
116910                 return layer && layer.enabled();
116911             }
116912
116913             // add listeners
116914             context.features()
116915                 .on('change.map_features', section.reRender);
116916
116917             return section;
116918         }
116919
116920         function uiSectionMapStyleOptions(context) {
116921
116922             var section = uiSection('fill-area', context)
116923                 .title(_t('map_data.style_options'))
116924                 .disclosureContent(renderDisclosureContent)
116925                 .expandedByDefault(false);
116926
116927             function renderDisclosureContent(selection) {
116928                 var container = selection.selectAll('.layer-fill-list')
116929                     .data([0]);
116930
116931                 container.enter()
116932                     .append('ul')
116933                     .attr('class', 'layer-list layer-fill-list')
116934                     .merge(container)
116935                     .call(drawListItems, context.map().areaFillOptions, 'radio', 'area_fill', setFill, isActiveFill);
116936
116937                 var container2 = selection.selectAll('.layer-visual-diff-list')
116938                     .data([0]);
116939
116940                 container2.enter()
116941                     .append('ul')
116942                     .attr('class', 'layer-list layer-visual-diff-list')
116943                     .merge(container2)
116944                     .call(drawListItems, ['highlight_edits'], 'checkbox', 'visual_diff', toggleHighlightEdited, function() {
116945                         return context.surface().classed('highlight-edited');
116946                     });
116947             }
116948
116949             function drawListItems(selection, data, type, name, change, active) {
116950                 var items = selection.selectAll('li')
116951                     .data(data);
116952
116953                 // Exit
116954                 items.exit()
116955                     .remove();
116956
116957                 // Enter
116958                 var enter = items.enter()
116959                     .append('li')
116960                     .call(uiTooltip()
116961                         .title(function(d) {
116962                             return _t(name + '.' + d + '.tooltip');
116963                         })
116964                         .keys(function(d) {
116965                             var key = (d === 'wireframe' ? _t('area_fill.wireframe.key') : null);
116966                             if (d === 'highlight_edits') key = _t('map_data.highlight_edits.key');
116967                             return key ? [key] : null;
116968                         })
116969                         .placement('top')
116970                     );
116971
116972                 var label = enter
116973                     .append('label');
116974
116975                 label
116976                     .append('input')
116977                     .attr('type', type)
116978                     .attr('name', name)
116979                     .on('change', change);
116980
116981                 label
116982                     .append('span')
116983                     .text(function(d) { return _t(name + '.' + d + '.description'); });
116984
116985                 // Update
116986                 items = items
116987                     .merge(enter);
116988
116989                 items
116990                     .classed('active', active)
116991                     .selectAll('input')
116992                     .property('checked', active)
116993                     .property('indeterminate', false);
116994             }
116995
116996             function isActiveFill(d) {
116997                 return context.map().activeAreaFill() === d;
116998             }
116999
117000             function toggleHighlightEdited() {
117001                 event.preventDefault();
117002                 context.map().toggleHighlightEdited();
117003             }
117004
117005             function setFill(d) {
117006                 context.map().activeAreaFill(d);
117007             }
117008
117009             context.map()
117010                 .on('changeHighlighting.ui_style, changeAreaFill.ui_style', section.reRender);
117011
117012             return section;
117013         }
117014
117015         function uiSectionPhotoOverlays(context) {
117016
117017             var layers = context.layers();
117018
117019             var section = uiSection('photo-overlays', context)
117020                 .title(_t('photo_overlays.title'))
117021                 .disclosureContent(renderDisclosureContent)
117022                 .expandedByDefault(false);
117023
117024             function renderDisclosureContent(selection) {
117025                 var container = selection.selectAll('.photo-overlay-container')
117026                     .data([0]);
117027
117028                 container.enter()
117029                     .append('div')
117030                     .attr('class', 'photo-overlay-container')
117031                     .merge(container)
117032                     .call(drawPhotoItems)
117033                     .call(drawPhotoTypeItems);
117034             }
117035
117036             function drawPhotoItems(selection) {
117037                 var photoKeys = context.photos().overlayLayerIDs();
117038                 var photoLayers = layers.all().filter(function(obj) { return photoKeys.indexOf(obj.id) !== -1; });
117039                 var data = photoLayers.filter(function(obj) { return obj.layer.supported(); });
117040
117041                 function layerSupported(d) {
117042                     return d.layer && d.layer.supported();
117043                 }
117044                 function layerEnabled(d) {
117045                     return layerSupported(d) && d.layer.enabled();
117046                 }
117047
117048                 var ul = selection
117049                     .selectAll('.layer-list-photos')
117050                     .data([0]);
117051
117052                 ul = ul.enter()
117053                     .append('ul')
117054                     .attr('class', 'layer-list layer-list-photos')
117055                     .merge(ul);
117056
117057                 var li = ul.selectAll('.list-item-photos')
117058                     .data(data);
117059
117060                 li.exit()
117061                     .remove();
117062
117063                 var liEnter = li.enter()
117064                     .append('li')
117065                     .attr('class', function(d) {
117066                         var classes = 'list-item-photos list-item-' + d.id;
117067                         if (d.id === 'mapillary-signs' || d.id === 'mapillary-map-features') {
117068                             classes += ' indented';
117069                         }
117070                         return classes;
117071                     });
117072
117073                 var labelEnter = liEnter
117074                     .append('label')
117075                     .each(function(d) {
117076                         var titleID;
117077                         if (d.id === 'mapillary-signs') titleID = 'mapillary.signs.tooltip';
117078                         else if (d.id === 'mapillary') titleID = 'mapillary_images.tooltip';
117079                         else if (d.id === 'openstreetcam') titleID = 'openstreetcam_images.tooltip';
117080                         else titleID = d.id.replace(/-/g, '_') + '.tooltip';
117081                         select(this)
117082                             .call(uiTooltip()
117083                                 .title(_t(titleID))
117084                                 .placement('top')
117085                             );
117086                     });
117087
117088                 labelEnter
117089                     .append('input')
117090                     .attr('type', 'checkbox')
117091                     .on('change', function(d) { toggleLayer(d.id); });
117092
117093                 labelEnter
117094                     .append('span')
117095                     .text(function(d) {
117096                         var id = d.id;
117097                         if (id === 'mapillary-signs') id = 'photo_overlays.traffic_signs';
117098                         return _t(id.replace(/-/g, '_') + '.title');
117099                     });
117100
117101
117102                 // Update
117103                 li
117104                     .merge(liEnter)
117105                     .classed('active', layerEnabled)
117106                     .selectAll('input')
117107                     .property('checked', layerEnabled);
117108             }
117109
117110             function drawPhotoTypeItems(selection) {
117111                 var data = context.photos().allPhotoTypes();
117112
117113                 function typeEnabled(d) {
117114                     return context.photos().showsPhotoType(d);
117115                 }
117116
117117                 var ul = selection
117118                     .selectAll('.layer-list-photo-types')
117119                     .data(context.photos().shouldFilterByPhotoType() ? [0] : []);
117120
117121                 ul.exit()
117122                     .remove();
117123
117124                 ul = ul.enter()
117125                     .append('ul')
117126                     .attr('class', 'layer-list layer-list-photo-types')
117127                     .merge(ul);
117128
117129                 var li = ul.selectAll('.list-item-photo-types')
117130                     .data(data);
117131
117132                 li.exit()
117133                     .remove();
117134
117135                 var liEnter = li.enter()
117136                     .append('li')
117137                     .attr('class', function(d) {
117138                         return 'list-item-photo-types list-item-' + d;
117139                     });
117140
117141                 var labelEnter = liEnter
117142                     .append('label')
117143                     .each(function(d) {
117144                         select(this)
117145                             .call(uiTooltip()
117146                                 .title(_t('photo_overlays.photo_type.' + d + '.tooltip'))
117147                                 .placement('top')
117148                             );
117149                     });
117150
117151                 labelEnter
117152                     .append('input')
117153                     .attr('type', 'checkbox')
117154                     .on('change', function(d) {
117155                         context.photos().togglePhotoType(d);
117156                     });
117157
117158                 labelEnter
117159                     .append('span')
117160                     .text(function(d) {
117161                         return _t('photo_overlays.photo_type.' + d + '.title');
117162                     });
117163
117164
117165                 // Update
117166                 li
117167                     .merge(liEnter)
117168                     .classed('active', typeEnabled)
117169                     .selectAll('input')
117170                     .property('checked', typeEnabled);
117171             }
117172
117173             function toggleLayer(which) {
117174                 setLayer(which, !showsLayer(which));
117175             }
117176
117177             function showsLayer(which) {
117178                 var layer = layers.layer(which);
117179                 if (layer) {
117180                     return layer.enabled();
117181                 }
117182                 return false;
117183             }
117184
117185             function setLayer(which, enabled) {
117186                 var layer = layers.layer(which);
117187                 if (layer) {
117188                     layer.enabled(enabled);
117189                 }
117190             }
117191
117192             context.layers().on('change.uiSectionPhotoOverlays', section.reRender);
117193             context.photos().on('change.uiSectionPhotoOverlays', section.reRender);
117194
117195             return section;
117196         }
117197
117198         function uiPaneMapData(context) {
117199
117200             var mapDataPane = uiPane('map-data', context)
117201                 .key(_t('map_data.key'))
117202                 .title(_t('map_data.title'))
117203                 .description(_t('map_data.description'))
117204                 .iconName('iD-icon-data')
117205                 .sections([
117206                     uiSectionDataLayers(context),
117207                     uiSectionPhotoOverlays(context),
117208                     uiSectionMapStyleOptions(context),
117209                     uiSectionMapFeatures(context)
117210                 ]);
117211
117212             return mapDataPane;
117213         }
117214
117215         function uiSectionPrivacy(context) {
117216
117217             let section = uiSection('preferences-third-party', context)
117218               .title(_t('preferences.privacy.title'))
117219               .disclosureContent(renderDisclosureContent);
117220
117221             let _showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
117222
117223             function renderDisclosureContent(selection) {
117224               // enter
117225               let privacyOptionsListEnter = selection.selectAll('.privacy-options-list')
117226                 .data([0])
117227                 .enter()
117228                 .append('ul')
117229                 .attr('class', 'layer-list privacy-options-list');
117230
117231               let thirdPartyIconsEnter = privacyOptionsListEnter
117232                 .append('li')
117233                 .attr('class', 'privacy-third-party-icons-item')
117234                 .append('label')
117235                 .call(uiTooltip()
117236                   .title(_t('preferences.privacy.third_party_icons.tooltip'))
117237                   .placement('bottom')
117238                 );
117239
117240               thirdPartyIconsEnter
117241                 .append('input')
117242                 .attr('type', 'checkbox')
117243                 .on('change', () => {
117244                   event.preventDefault();
117245                   _showThirdPartyIcons = (_showThirdPartyIcons === 'true') ? 'false' : 'true';
117246                   corePreferences('preferences.privacy.thirdpartyicons', _showThirdPartyIcons);
117247                   update();
117248                 });
117249
117250               thirdPartyIconsEnter
117251                 .append('span')
117252                 .text(_t('preferences.privacy.third_party_icons.description'));
117253
117254
117255               // Privacy Policy link
117256               selection.selectAll('.privacy-link')
117257                 .data([0])
117258                 .enter()
117259                 .append('div')
117260                 .attr('class', 'privacy-link')
117261                 .append('a')
117262                 .attr('target', '_blank')
117263                 .call(svgIcon('#iD-icon-out-link', 'inline'))
117264                 .attr('href', 'https://github.com/openstreetmap/iD/blob/release/PRIVACY.md')
117265                 .append('span')
117266                 .text(_t('preferences.privacy.privacy_link'));
117267
117268               update();
117269
117270
117271               function update() {
117272                 selection.selectAll('.privacy-third-party-icons-item')
117273                   .classed('active', (_showThirdPartyIcons === 'true'))
117274                   .select('input')
117275                   .property('checked', (_showThirdPartyIcons === 'true'));
117276               }
117277             }
117278
117279             return section;
117280         }
117281
117282         function uiPanePreferences(context) {
117283
117284           let preferencesPane = uiPane('preferences', context)
117285             .key(_t('preferences.key'))
117286             .title(_t('preferences.title'))
117287             .description(_t('preferences.description'))
117288             .iconName('fas-user-cog')
117289             .sections([
117290                 uiSectionPrivacy(context)
117291             ]);
117292
117293           return preferencesPane;
117294         }
117295
117296         function uiInit(context) {
117297             var _initCounter = 0;
117298             var _needWidth = {};
117299
117300             var _lastPointerType;
117301
117302
117303             function render(container) {
117304
117305                 container
117306                     .on('click.ui', function() {
117307                         // we're only concerned with the primary mouse button
117308                         if (event.button !== 0) return;
117309
117310                         if (!event.composedPath) return;
117311
117312                         // some targets have default click events we don't want to override
117313                         var isOkayTarget = event.composedPath().some(function(node) {
117314                             // clicking <label> affects its <input> by default
117315                             return node.nodeName === 'LABEL' ||
117316                                 // clicking <a> opens a hyperlink by default
117317                                 node.nodeName === 'A';
117318                         });
117319                         if (isOkayTarget) return;
117320
117321                         // disable double-tap-to-zoom on touchscreens
117322                         event.preventDefault();
117323                     });
117324
117325                 var detected = utilDetect();
117326
117327                 // only WebKit supports gesture events
117328                 if ('GestureEvent' in window &&
117329                     // Listening for gesture events on iOS 13.4+ breaks double-tapping,
117330                     // but we only need to do this on desktop Safari anyway. – #7694
117331                     !detected.isMobileWebKit) {
117332
117333                     // On iOS we disable pinch-to-zoom of the UI via the `touch-action`
117334                     // CSS property, but on desktop Safari we need to manually cancel the
117335                     // default gesture events.
117336                     container.on('gesturestart.ui gesturechange.ui gestureend.ui', function() {
117337                         // disable pinch-to-zoom of the UI via multitouch trackpads on macOS Safari
117338                         event.preventDefault();
117339                     });
117340                 }
117341
117342                 if ('PointerEvent' in window) {
117343                     select(window)
117344                         .on('pointerdown.ui pointerup.ui', function() {
117345                             var pointerType = event.pointerType || 'mouse';
117346                             if (_lastPointerType !== pointerType) {
117347                                 _lastPointerType = pointerType;
117348                                 container
117349                                     .attr('pointer', pointerType);
117350                             }
117351                         }, true);
117352                 } else {
117353                     _lastPointerType = 'mouse';
117354                     container
117355                         .attr('pointer', 'mouse');
117356                 }
117357
117358                 container
117359                     .attr('dir', _mainLocalizer.textDirection());
117360
117361                 // setup fullscreen keybindings (no button shown at this time)
117362                 container
117363                     .call(uiFullScreen(context));
117364
117365                 var map = context.map();
117366                 map.redrawEnable(false);  // don't draw until we've set zoom/lat/long
117367
117368                 map
117369                     .on('hitMinZoom.ui', function() {
117370                         ui.flash.text(_t('cannot_zoom'))();
117371                     });
117372
117373                 container
117374                     .append('svg')
117375                     .attr('id', 'ideditor-defs')
117376                     .call(svgDefs(context));
117377
117378                 container
117379                     .append('div')
117380                     .attr('class', 'sidebar')
117381                     .call(ui.sidebar);
117382
117383                 var content = container
117384                     .append('div')
117385                     .attr('class', 'main-content active');
117386
117387                 // Top toolbar
117388                 content
117389                     .append('div')
117390                     .attr('class', 'top-toolbar-wrap')
117391                     .append('div')
117392                     .attr('class', 'top-toolbar fillD')
117393                     .call(uiTopToolbar(context));
117394
117395                 content
117396                     .append('div')
117397                     .attr('class', 'main-map')
117398                     .attr('dir', 'ltr')
117399                     .call(map);
117400
117401                 content
117402                     .append('div')
117403                     .attr('class', 'spinner')
117404                     .call(uiSpinner(context));
117405
117406                 // Add attribution and footer
117407                 var about = content
117408                     .append('div')
117409                     .attr('class', 'map-footer');
117410
117411                 about
117412                     .append('div')
117413                     .attr('class', 'attribution-wrap')
117414                     .attr('dir', 'ltr')
117415                     .call(uiAttribution(context));
117416
117417                 about
117418                     .append('div')
117419                     .attr('class', 'api-status')
117420                     .call(uiStatus(context));
117421
117422
117423                 var footer = about
117424                     .append('div')
117425                     .attr('class', 'map-footer-bar fillD');
117426
117427                 footer
117428                     .append('div')
117429                     .attr('class', 'flash-wrap footer-hide');
117430
117431                 var footerWrap = footer
117432                     .append('div')
117433                     .attr('class', 'main-footer-wrap footer-show');
117434
117435                 footerWrap
117436                     .append('div')
117437                     .attr('class', 'scale-block')
117438                     .call(uiScale(context));
117439
117440                 var aboutList = footerWrap
117441                     .append('div')
117442                     .attr('class', 'info-block')
117443                     .append('ul')
117444                     .attr('class', 'map-footer-list');
117445
117446                 if (!context.embed()) {
117447                     aboutList
117448                         .call(uiAccount(context));
117449                 }
117450
117451                 aboutList
117452                     .append('li')
117453                     .attr('class', 'version')
117454                     .call(uiVersion(context));
117455
117456                 var issueLinks = aboutList
117457                     .append('li');
117458
117459                 issueLinks
117460                     .append('a')
117461                     .attr('target', '_blank')
117462                     .attr('href', 'https://github.com/openstreetmap/iD/issues')
117463                     .call(svgIcon('#iD-icon-bug', 'light'))
117464                     .call(uiTooltip().title(_t('report_a_bug')).placement('top'));
117465
117466                 issueLinks
117467                     .append('a')
117468                     .attr('target', '_blank')
117469                     .attr('href', 'https://github.com/openstreetmap/iD/blob/develop/CONTRIBUTING.md#translating')
117470                     .call(svgIcon('#iD-icon-translate', 'light'))
117471                     .call(uiTooltip().title(_t('help_translate')).placement('top'));
117472
117473                 aboutList
117474                     .append('li')
117475                     .attr('class', 'feature-warning')
117476                     .attr('tabindex', -1)
117477                     .call(uiFeatureInfo(context));
117478
117479                 aboutList
117480                     .append('li')
117481                     .attr('class', 'issues-info')
117482                     .attr('tabindex', -1)
117483                     .call(uiIssuesInfo(context));
117484
117485                 var apiConnections = context.apiConnections();
117486                 if (apiConnections && apiConnections.length > 1) {
117487                     aboutList
117488                         .append('li')
117489                         .attr('class', 'source-switch')
117490                         .attr('tabindex', -1)
117491                         .call(uiSourceSwitch(context)
117492                             .keys(apiConnections)
117493                         );
117494                 }
117495
117496                 aboutList
117497                     .append('li')
117498                     .attr('class', 'user-list')
117499                     .attr('tabindex', -1)
117500                     .call(uiContributors(context));
117501
117502
117503                 // Setup map dimensions and move map to initial center/zoom.
117504                 // This should happen after .main-content and toolbars exist.
117505                 ui.onResize();
117506                 map.redrawEnable(true);
117507
117508                 ui.hash = behaviorHash(context);
117509                 ui.hash();
117510                 if (!ui.hash.hadHash) {
117511                     map.centerZoom([0, 0], 2);
117512                 }
117513
117514
117515                 var overMap = content
117516                     .append('div')
117517                     .attr('class', 'over-map');
117518
117519                 // Map controls
117520                 var controls = overMap
117521                     .append('div')
117522                     .attr('class', 'map-controls');
117523
117524                 controls
117525                     .append('div')
117526                     .attr('class', 'map-control zoombuttons')
117527                     .call(uiZoom(context));
117528
117529                 controls
117530                     .append('div')
117531                     .attr('class', 'map-control zoom-to-selection-control')
117532                     .call(uiZoomToSelection(context));
117533
117534                 controls
117535                     .append('div')
117536                     .attr('class', 'map-control geolocate-control')
117537                     .call(uiGeolocate(context));
117538
117539                 // Add panes
117540                 // This should happen after map is initialized, as some require surface()
117541                 var panes = overMap
117542                     .append('div')
117543                     .attr('class', 'map-panes');
117544
117545                 var uiPanes = [
117546                     uiPaneBackground(context),
117547                     uiPaneMapData(context),
117548                     uiPaneIssues(context),
117549                     uiPanePreferences(context),
117550                     uiPaneHelp(context)
117551                 ];
117552
117553                 uiPanes.forEach(function(pane) {
117554                     controls
117555                         .append('div')
117556                         .attr('class', 'map-control map-pane-control ' + pane.id + '-control')
117557                         .call(pane.renderToggleButton);
117558
117559                     panes
117560                         .call(pane.renderPane);
117561                 });
117562
117563                 ui.info = uiInfo(context);
117564
117565                 // Add absolutely-positioned elements that sit on top of the map
117566                 // This should happen after the map is ready (center/zoom)
117567                 overMap
117568                     .call(uiMapInMap(context))
117569                     .call(ui.info)
117570                     .call(uiNotice(context));
117571
117572
117573                 overMap
117574                     .append('div')
117575                     .attr('class', 'photoviewer')
117576                     .classed('al', true)       // 'al'=left,  'ar'=right
117577                     .classed('hide', true)
117578                     .call(ui.photoviewer);
117579
117580
117581                 // Bind events
117582                 window.onbeforeunload = function() {
117583                     return context.save();
117584                 };
117585                 window.onunload = function() {
117586                     context.history().unlock();
117587                 };
117588
117589                 select(window)
117590                     .on('resize.editor', ui.onResize);
117591
117592
117593                 var panPixels = 80;
117594                 context.keybinding()
117595                     .on('⌫', function() { event.preventDefault(); })
117596                     .on([_t('sidebar.key'), '`', '²', '@'], ui.sidebar.toggle)   // #5663, #6864 - common QWERTY, AZERTY
117597                     .on('←', pan([panPixels, 0]))
117598                     .on('↑', pan([0, panPixels]))
117599                     .on('→', pan([-panPixels, 0]))
117600                     .on('↓', pan([0, -panPixels]))
117601                     .on(uiCmd('⌘←'), pan([map.dimensions()[0], 0]))
117602                     .on(uiCmd('⌘↑'), pan([0, map.dimensions()[1]]))
117603                     .on(uiCmd('⌘→'), pan([-map.dimensions()[0], 0]))
117604                     .on(uiCmd('⌘↓'), pan([0, -map.dimensions()[1]]))
117605                     .on(uiCmd('⌘' + _t('background.key')), function quickSwitch() {
117606                         if (event) {
117607                             event.stopImmediatePropagation();
117608                             event.preventDefault();
117609                         }
117610                         var previousBackground = context.background().findSource(corePreferences('background-last-used-toggle'));
117611                         if (previousBackground) {
117612                             var currentBackground = context.background().baseLayerSource();
117613                             corePreferences('background-last-used-toggle', currentBackground.id);
117614                             corePreferences('background-last-used', previousBackground.id);
117615                             context.background().baseLayerSource(previousBackground);
117616                         }
117617                     })
117618                     .on(_t('area_fill.wireframe.key'), function toggleWireframe() {
117619                         event.preventDefault();
117620                         event.stopPropagation();
117621                         context.map().toggleWireframe();
117622                     })
117623                     .on(uiCmd('⌥' + _t('area_fill.wireframe.key')), function toggleOsmData() {
117624                         event.preventDefault();
117625                         event.stopPropagation();
117626
117627                         // Don't allow layer changes while drawing - #6584
117628                         var mode = context.mode();
117629                         if (mode && /^draw/.test(mode.id)) return;
117630
117631                         var layer = context.layers().layer('osm');
117632                         if (layer) {
117633                             layer.enabled(!layer.enabled());
117634                             if (!layer.enabled()) {
117635                                 context.enter(modeBrowse(context));
117636                             }
117637                         }
117638                     })
117639                     .on(_t('map_data.highlight_edits.key'), function toggleHighlightEdited() {
117640                         event.preventDefault();
117641                         context.map().toggleHighlightEdited();
117642                     });
117643
117644                 context
117645                     .on('enter.editor', function(entered) {
117646                         container
117647                             .classed('mode-' + entered.id, true);
117648                     })
117649                     .on('exit.editor', function(exited) {
117650                         container
117651                             .classed('mode-' + exited.id, false);
117652                     });
117653
117654                 context.enter(modeBrowse(context));
117655
117656                 if (!_initCounter++) {
117657                     if (!ui.hash.startWalkthrough) {
117658                         context.container()
117659                             .call(uiSplash(context))
117660                             .call(uiRestore(context));
117661                     }
117662
117663                     context.container()
117664                         .call(uiShortcuts(context));
117665                 }
117666
117667                 var osm = context.connection();
117668                 var auth = uiLoading(context).message(_t('loading_auth')).blocking(true);
117669
117670                 if (osm && auth) {
117671                     osm
117672                         .on('authLoading.ui', function() {
117673                             context.container()
117674                                 .call(auth);
117675                         })
117676                         .on('authDone.ui', function() {
117677                             auth.close();
117678                         });
117679                 }
117680
117681                 _initCounter++;
117682
117683                 if (ui.hash.startWalkthrough) {
117684                     ui.hash.startWalkthrough = false;
117685                     context.container().call(uiIntro(context));
117686                 }
117687
117688
117689                 function pan(d) {
117690                     return function() {
117691                         if (event.shiftKey) return;
117692                         if (context.container().select('.combobox').size()) return;
117693                         event.preventDefault();
117694                         context.map().pan(d, 100);
117695                     };
117696                 }
117697
117698             }
117699
117700
117701             let ui = {};
117702
117703             let _loadPromise;
117704             // renders the iD interface into the container node
117705             ui.ensureLoaded = () => {
117706
117707                 if (_loadPromise) return _loadPromise;
117708
117709                 return _loadPromise = Promise.all([
117710                         // must have strings and presets before loading the UI
117711                         _mainLocalizer.ensureLoaded(),
117712                         _mainPresetIndex.ensureLoaded()
117713                     ])
117714                     .then(() => {
117715                         if (!context.container().empty()) render(context.container());
117716                     })
117717                     .catch(err => console.error(err));  // eslint-disable-line
117718             };
117719
117720
117721             // `ui.restart()` will destroy and rebuild the entire iD interface,
117722             // for example to switch the locale while iD is running.
117723             ui.restart = function() {
117724                 context.keybinding().clear();
117725
117726                 _loadPromise = null;
117727
117728                 context.container().selectAll('*').remove();
117729
117730                 ui.ensureLoaded();
117731             };
117732
117733             ui.lastPointerType = function() {
117734                 return _lastPointerType;
117735             };
117736
117737             ui.flash = uiFlash(context);
117738
117739             ui.sidebar = uiSidebar(context);
117740
117741             ui.photoviewer = uiPhotoviewer(context);
117742
117743             ui.onResize = function(withPan) {
117744                 var map = context.map();
117745
117746                 // Recalc dimensions of map and sidebar.. (`true` = force recalc)
117747                 // This will call `getBoundingClientRect` and trigger reflow,
117748                 //  but the values will be cached for later use.
117749                 var mapDimensions = utilGetDimensions(context.container().select('.main-content'), true);
117750                 utilGetDimensions(context.container().select('.sidebar'), true);
117751
117752                 if (withPan !== undefined) {
117753                     map.redrawEnable(false);
117754                     map.pan(withPan);
117755                     map.redrawEnable(true);
117756                 }
117757                 map.dimensions(mapDimensions);
117758
117759                 ui.photoviewer.onMapResize();
117760
117761                 // check if header or footer have overflowed
117762                 ui.checkOverflow('.top-toolbar');
117763                 ui.checkOverflow('.map-footer-bar');
117764
117765                 // Use outdated code so it works on Explorer
117766                 var resizeWindowEvent = document.createEvent('Event');
117767
117768                 resizeWindowEvent.initEvent('resizeWindow', true, true);
117769
117770                 document.dispatchEvent(resizeWindowEvent);
117771             };
117772
117773
117774             // Call checkOverflow when resizing or whenever the contents change.
117775             ui.checkOverflow = function(selector, reset) {
117776                 if (reset) {
117777                     delete _needWidth[selector];
117778                 }
117779
117780                 var element = select(selector);
117781                 var scrollWidth = element.property('scrollWidth');
117782                 var clientWidth = element.property('clientWidth');
117783                 var needed = _needWidth[selector] || scrollWidth;
117784
117785                 if (scrollWidth > clientWidth) {    // overflow happening
117786                     element.classed('narrow', true);
117787                     if (!_needWidth[selector]) {
117788                         _needWidth[selector] = scrollWidth;
117789                     }
117790
117791                 } else if (scrollWidth >= needed) {
117792                     element.classed('narrow', false);
117793                 }
117794             };
117795
117796             ui.togglePanes = function(showPane) {
117797                 var shownPanes = context.container().selectAll('.map-pane.shown');
117798
117799                 var side = _mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left';
117800
117801                 shownPanes
117802                     .classed('shown', false);
117803
117804                 context.container().selectAll('.map-pane-control button')
117805                     .classed('active', false);
117806
117807                 if (showPane) {
117808                     shownPanes
117809                         .style('display', 'none')
117810                         .style(side, '-500px');
117811
117812                     context.container().selectAll('.' + showPane.attr('pane') + '-control button')
117813                         .classed('active', true);
117814
117815                     showPane
117816                         .classed('shown', true)
117817                         .style('display', 'block');
117818                     if (shownPanes.empty()) {
117819                         showPane
117820                             .style('display', 'block')
117821                             .style(side, '-500px')
117822                             .transition()
117823                             .duration(200)
117824                             .style(side, '0px');
117825                     } else {
117826                         showPane
117827                             .style(side, '0px');
117828                     }
117829                 } else {
117830                     shownPanes
117831                         .style('display', 'block')
117832                         .style(side, '0px')
117833                         .transition()
117834                         .duration(200)
117835                         .style(side, '-500px')
117836                         .on('end', function() {
117837                             select(this).style('display', 'none');
117838                         });
117839                 }
117840             };
117841
117842
117843             var _editMenu = uiEditMenu(context);
117844
117845             ui.editMenu = function() {
117846                 return _editMenu;
117847             };
117848
117849             ui.showEditMenu = function(anchorPoint, triggerType, operations) {
117850
117851                 // remove any displayed menu
117852                 ui.closeEditMenu();
117853
117854                 if (!operations && context.mode().operations) operations = context.mode().operations();
117855                 if (!operations || !operations.length) return;
117856
117857                 // disable menu if in wide selection, for example
117858                 if (!context.map().editableDataEnabled()) return;
117859
117860                 var surfaceNode = context.surface().node();
117861                 if (surfaceNode.focus) {   // FF doesn't support it
117862                     // focus the surface or else clicking off the menu may not trigger modeBrowse
117863                     surfaceNode.focus();
117864                 }
117865
117866                 operations.forEach(function(operation) {
117867                     if (operation.point) operation.point(anchorPoint);
117868                 });
117869
117870                 _editMenu
117871                     .anchorLoc(anchorPoint)
117872                     .triggerType(triggerType)
117873                     .operations(operations);
117874
117875                 // render the menu
117876                 context.map().supersurface.call(_editMenu);
117877             };
117878
117879             ui.closeEditMenu = function() {
117880                 // remove any existing menu no matter how it was added
117881                 context.map().supersurface
117882                     .select('.edit-menu').remove();
117883             };
117884
117885
117886             var _saveLoading = select(null);
117887
117888             context.uploader()
117889                 .on('saveStarted.ui', function() {
117890                     _saveLoading = uiLoading(context)
117891                         .message(_t('save.uploading'))
117892                         .blocking(true);
117893                     context.container().call(_saveLoading);  // block input during upload
117894                 })
117895                 .on('saveEnded.ui', function() {
117896                     _saveLoading.close();
117897                     _saveLoading = select(null);
117898                 });
117899
117900             return ui;
117901         }
117902
117903         function coreContext() {
117904           const dispatch$1 = dispatch('enter', 'exit', 'change');
117905           let context = utilRebind({}, dispatch$1, 'on');
117906           let _deferred = new Set();
117907
117908           context.version = '2.18.1';
117909           context.privacyVersion = '20200407';
117910
117911           // iD will alter the hash so cache the parameters intended to setup the session
117912           context.initialHashParams = window.location.hash ? utilStringQs(window.location.hash) : {};
117913
117914           context.isFirstSession = !corePreferences('sawSplash') && !corePreferences('sawPrivacyVersion');
117915
117916
117917           /* Changeset */
117918           // An osmChangeset object. Not loaded until needed.
117919           context.changeset = null;
117920
117921           let _defaultChangesetComment = context.initialHashParams.comment;
117922           let _defaultChangesetSource = context.initialHashParams.source;
117923           let _defaultChangesetHashtags = context.initialHashParams.hashtags;
117924           context.defaultChangesetComment = function(val) {
117925             if (!arguments.length) return _defaultChangesetComment;
117926             _defaultChangesetComment = val;
117927             return context;
117928           };
117929           context.defaultChangesetSource = function(val) {
117930             if (!arguments.length) return _defaultChangesetSource;
117931             _defaultChangesetSource = val;
117932             return context;
117933           };
117934           context.defaultChangesetHashtags = function(val) {
117935             if (!arguments.length) return _defaultChangesetHashtags;
117936             _defaultChangesetHashtags = val;
117937             return context;
117938           };
117939
117940           /* Document title */
117941           /* (typically shown as the label for the browser window/tab) */
117942
117943           // If true, iD will update the title based on what the user is doing
117944           let _setsDocumentTitle = true;
117945           context.setsDocumentTitle = function(val) {
117946             if (!arguments.length) return _setsDocumentTitle;
117947             _setsDocumentTitle = val;
117948             return context;
117949           };
117950           // The part of the title that is always the same
117951           let _documentTitleBase = document.title;
117952           context.documentTitleBase = function(val) {
117953             if (!arguments.length) return _documentTitleBase;
117954             _documentTitleBase = val;
117955             return context;
117956           };
117957
117958
117959           /* User interface and keybinding */
117960           let _ui;
117961           context.ui = () => _ui;
117962           context.lastPointerType = () => _ui.lastPointerType();
117963
117964           let _keybinding = utilKeybinding('context');
117965           context.keybinding = () => _keybinding;
117966           select(document).call(_keybinding);
117967
117968
117969           /* Straight accessors. Avoid using these if you can. */
117970           let _connection;
117971           let _history;
117972           let _validator;
117973           let _uploader;
117974           context.connection = () => _connection;
117975           context.history = () => _history;
117976           context.validator = () => _validator;
117977           context.uploader = () => _uploader;
117978
117979           /* Connection */
117980           context.preauth = (options) => {
117981             if (_connection) {
117982               _connection.switch(options);
117983             }
117984             return context;
117985           };
117986
117987           /* connection options for source switcher (optional) */
117988           let _apiConnections;
117989           context.apiConnections = function(val) {
117990             if (!arguments.length) return _apiConnections;
117991             _apiConnections = val;
117992             return context;
117993           };
117994
117995
117996           // A string or array or locale codes to prefer over the browser's settings
117997           context.locale = function(locale) {
117998             if (!arguments.length) return _mainLocalizer.localeCode();
117999             _mainLocalizer.preferredLocaleCodes(locale);
118000             return context;
118001           };
118002
118003
118004           function afterLoad(cid, callback) {
118005             return (err, result) => {
118006               if (err) {
118007                 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
118008                 if (err.status === 400 || err.status === 401 || err.status === 403) {
118009                   if (_connection) {
118010                     _connection.logout();
118011                   }
118012                 }
118013                 if (typeof callback === 'function') {
118014                   callback(err);
118015                 }
118016                 return;
118017
118018               } else if (_connection && _connection.getConnectionId() !== cid) {
118019                 if (typeof callback === 'function') {
118020                   callback({ message: 'Connection Switched', status: -1 });
118021                 }
118022                 return;
118023
118024               } else {
118025                 _history.merge(result.data, result.extent);
118026                 if (typeof callback === 'function') {
118027                   callback(err, result);
118028                 }
118029                 return;
118030               }
118031             };
118032           }
118033
118034
118035           context.loadTiles = (projection, callback) => {
118036             const handle = window.requestIdleCallback(() => {
118037               _deferred.delete(handle);
118038               if (_connection && context.editableDataEnabled()) {
118039                 const cid = _connection.getConnectionId();
118040                 _connection.loadTiles(projection, afterLoad(cid, callback));
118041               }
118042             });
118043             _deferred.add(handle);
118044           };
118045
118046           context.loadTileAtLoc = (loc, callback) => {
118047             const handle = window.requestIdleCallback(() => {
118048               _deferred.delete(handle);
118049               if (_connection && context.editableDataEnabled()) {
118050                 const cid = _connection.getConnectionId();
118051                 _connection.loadTileAtLoc(loc, afterLoad(cid, callback));
118052               }
118053             });
118054             _deferred.add(handle);
118055           };
118056
118057           context.loadEntity = (entityID, callback) => {
118058             if (_connection) {
118059               const cid = _connection.getConnectionId();
118060               _connection.loadEntity(entityID, afterLoad(cid, callback));
118061             }
118062           };
118063
118064           context.zoomToEntity = (entityID, zoomTo) => {
118065             if (zoomTo !== false) {
118066               context.loadEntity(entityID, (err, result) => {
118067                 if (err) return;
118068                 const entity = result.data.find(e => e.id === entityID);
118069                 if (entity) {
118070                   _map.zoomTo(entity);
118071                 }
118072               });
118073             }
118074
118075             _map.on('drawn.zoomToEntity', () => {
118076               if (!context.hasEntity(entityID)) return;
118077               _map.on('drawn.zoomToEntity', null);
118078               context.on('enter.zoomToEntity', null);
118079               context.enter(modeSelect(context, [entityID]));
118080             });
118081
118082             context.on('enter.zoomToEntity', () => {
118083               if (_mode.id !== 'browse') {
118084                 _map.on('drawn.zoomToEntity', null);
118085                 context.on('enter.zoomToEntity', null);
118086               }
118087             });
118088           };
118089
118090           let _minEditableZoom = 16;
118091           context.minEditableZoom = function(val) {
118092             if (!arguments.length) return _minEditableZoom;
118093             _minEditableZoom = val;
118094             if (_connection) {
118095               _connection.tileZoom(val);
118096             }
118097             return context;
118098           };
118099
118100           // String length limits in Unicode characters, not JavaScript UTF-16 code units
118101           context.maxCharsForTagKey = () => 255;
118102           context.maxCharsForTagValue = () => 255;
118103           context.maxCharsForRelationRole = () => 255;
118104
118105           function cleanOsmString(val, maxChars) {
118106             // be lenient with input
118107             if (val === undefined || val === null) {
118108               val = '';
118109             } else {
118110               val = val.toString();
118111             }
118112
118113             // remove whitespace
118114             val = val.trim();
118115
118116             // use the canonical form of the string
118117             if (val.normalize) val = val.normalize('NFC');
118118
118119             // trim to the number of allowed characters
118120             return utilUnicodeCharsTruncated(val, maxChars);
118121           }
118122           context.cleanTagKey = (val) => cleanOsmString(val, context.maxCharsForTagKey());
118123           context.cleanTagValue = (val) => cleanOsmString(val, context.maxCharsForTagValue());
118124           context.cleanRelationRole = (val) => cleanOsmString(val, context.maxCharsForRelationRole());
118125
118126
118127           /* History */
118128           let _inIntro = false;
118129           context.inIntro = function(val) {
118130             if (!arguments.length) return _inIntro;
118131             _inIntro = val;
118132             return context;
118133           };
118134
118135           // Immediately save the user's history to localstorage, if possible
118136           // This is called someteimes, but also on the `window.onbeforeunload` handler
118137           context.save = () => {
118138             // no history save, no message onbeforeunload
118139             if (_inIntro || context.container().select('.modal').size()) return;
118140
118141             let canSave;
118142             if (_mode && _mode.id === 'save') {
118143               canSave = false;
118144
118145               // Attempt to prevent user from creating duplicate changes - see #5200
118146               if (services.osm && services.osm.isChangesetInflight()) {
118147                 _history.clearSaved();
118148                 return;
118149               }
118150
118151             } else {
118152               canSave = context.selectedIDs().every(id => {
118153                 const entity = context.hasEntity(id);
118154                 return entity && !entity.isDegenerate();
118155               });
118156             }
118157
118158             if (canSave) {
118159               _history.save();
118160             }
118161             if (_history.hasChanges()) {
118162               return _t('save.unsaved_changes');
118163             }
118164           };
118165
118166           // Debounce save, since it's a synchronous localStorage write,
118167           // and history changes can happen frequently (e.g. when dragging).
118168           context.debouncedSave = debounce(context.save, 350);
118169
118170           function withDebouncedSave(fn) {
118171             return function() {
118172               const result = fn.apply(_history, arguments);
118173               context.debouncedSave();
118174               return result;
118175             };
118176           }
118177
118178
118179           /* Graph */
118180           context.hasEntity = (id) => _history.graph().hasEntity(id);
118181           context.entity = (id) => _history.graph().entity(id);
118182
118183
118184           /* Modes */
118185           let _mode;
118186           context.mode = () => _mode;
118187           context.enter = (newMode) => {
118188             if (_mode) {
118189               _mode.exit();
118190               dispatch$1.call('exit', this, _mode);
118191             }
118192
118193             _mode = newMode;
118194             _mode.enter();
118195             dispatch$1.call('enter', this, _mode);
118196           };
118197
118198           context.selectedIDs = () => (_mode && _mode.selectedIDs && _mode.selectedIDs()) || [];
118199           context.activeID = () => _mode && _mode.activeID && _mode.activeID();
118200
118201           let _selectedNoteID;
118202           context.selectedNoteID = function(noteID) {
118203             if (!arguments.length) return _selectedNoteID;
118204             _selectedNoteID = noteID;
118205             return context;
118206           };
118207
118208           // NOTE: Don't change the name of this until UI v3 is merged
118209           let _selectedErrorID;
118210           context.selectedErrorID = function(errorID) {
118211             if (!arguments.length) return _selectedErrorID;
118212             _selectedErrorID = errorID;
118213             return context;
118214           };
118215
118216
118217           /* Behaviors */
118218           context.install = (behavior) => context.surface().call(behavior);
118219           context.uninstall = (behavior) => context.surface().call(behavior.off);
118220
118221
118222           /* Copy/Paste */
118223           let _copyGraph;
118224           context.copyGraph = () => _copyGraph;
118225
118226           let _copyIDs = [];
118227           context.copyIDs = function(val) {
118228             if (!arguments.length) return _copyIDs;
118229             _copyIDs = val;
118230             _copyGraph = _history.graph();
118231             return context;
118232           };
118233
118234           let _copyLonLat;
118235           context.copyLonLat = function(val) {
118236             if (!arguments.length) return _copyLonLat;
118237             _copyLonLat = val;
118238             return context;
118239           };
118240
118241
118242           /* Background */
118243           let _background;
118244           context.background = () => _background;
118245
118246
118247           /* Features */
118248           let _features;
118249           context.features = () => _features;
118250           context.hasHiddenConnections = (id) => {
118251             const graph = _history.graph();
118252             const entity = graph.entity(id);
118253             return _features.hasHiddenConnections(entity, graph);
118254           };
118255
118256
118257           /* Photos */
118258           let _photos;
118259           context.photos = () => _photos;
118260
118261
118262           /* Map */
118263           let _map;
118264           context.map = () => _map;
118265           context.layers = () => _map.layers();
118266           context.surface = () => _map.surface;
118267           context.editableDataEnabled = () => _map.editableDataEnabled();
118268           context.surfaceRect = () => _map.surface.node().getBoundingClientRect();
118269           context.editable = () => {
118270             // don't allow editing during save
118271             const mode = context.mode();
118272             if (!mode || mode.id === 'save') return false;
118273             return _map.editableDataEnabled();
118274           };
118275
118276
118277           /* Debug */
118278           let _debugFlags = {
118279             tile: false,        // tile boundaries
118280             collision: false,   // label collision bounding boxes
118281             imagery: false,     // imagery bounding polygons
118282             target: false,      // touch targets
118283             downloaded: false   // downloaded data from osm
118284           };
118285           context.debugFlags = () => _debugFlags;
118286           context.getDebug = (flag) => flag && _debugFlags[flag];
118287           context.setDebug = function(flag, val) {
118288             if (arguments.length === 1) val = true;
118289             _debugFlags[flag] = val;
118290             dispatch$1.call('change');
118291             return context;
118292           };
118293
118294
118295           /* Container */
118296           let _container = select(null);
118297           context.container = function(val) {
118298             if (!arguments.length) return _container;
118299             _container = val;
118300             _container.classed('ideditor', true);
118301             return context;
118302           };
118303           context.containerNode = function(val) {
118304             if (!arguments.length) return context.container().node();
118305             context.container(select(val));
118306             return context;
118307           };
118308
118309           let _embed;
118310           context.embed = function(val) {
118311             if (!arguments.length) return _embed;
118312             _embed = val;
118313             return context;
118314           };
118315
118316
118317           /* Assets */
118318           let _assetPath = '';
118319           context.assetPath = function(val) {
118320             if (!arguments.length) return _assetPath;
118321             _assetPath = val;
118322             _mainFileFetcher.assetPath(val);
118323             return context;
118324           };
118325
118326           let _assetMap = {};
118327           context.assetMap = function(val) {
118328             if (!arguments.length) return _assetMap;
118329             _assetMap = val;
118330             _mainFileFetcher.assetMap(val);
118331             return context;
118332           };
118333
118334           context.asset = (val) => {
118335             if (/^http(s)?:\/\//i.test(val)) return val;
118336             const filename = _assetPath + val;
118337             return _assetMap[filename] || filename;
118338           };
118339
118340           context.imagePath = (val) => context.asset(`img/${val}`);
118341
118342
118343           /* reset (aka flush) */
118344           context.reset = context.flush = () => {
118345             context.debouncedSave.cancel();
118346
118347             Array.from(_deferred).forEach(handle => {
118348               window.cancelIdleCallback(handle);
118349               _deferred.delete(handle);
118350             });
118351
118352             Object.values(services).forEach(service => {
118353               if (service && typeof service.reset === 'function') {
118354                 service.reset(context);
118355               }
118356             });
118357
118358             context.changeset = null;
118359
118360             _validator.reset();
118361             _features.reset();
118362             _history.reset();
118363             _uploader.reset();
118364
118365             // don't leave stale state in the inspector
118366             context.container().select('.inspector-wrap *').remove();
118367
118368             return context;
118369           };
118370
118371
118372           /* Projections */
118373           context.projection = geoRawMercator();
118374           context.curtainProjection = geoRawMercator();
118375
118376
118377           /* Init */
118378           context.init = () => {
118379
118380             instantiateInternal();
118381
118382             initializeDependents();
118383
118384             return context;
118385
118386             // Load variables and properties. No property of `context` should be accessed
118387             // until this is complete since load statuses are indeterminate. The order
118388             // of instantiation shouldn't matter.
118389             function instantiateInternal() {
118390
118391               _history = coreHistory(context);
118392               context.graph = _history.graph;
118393               context.pauseChangeDispatch = _history.pauseChangeDispatch;
118394               context.resumeChangeDispatch = _history.resumeChangeDispatch;
118395               context.perform = withDebouncedSave(_history.perform);
118396               context.replace = withDebouncedSave(_history.replace);
118397               context.pop = withDebouncedSave(_history.pop);
118398               context.overwrite = withDebouncedSave(_history.overwrite);
118399               context.undo = withDebouncedSave(_history.undo);
118400               context.redo = withDebouncedSave(_history.redo);
118401
118402               _validator = coreValidator(context);
118403               _uploader = coreUploader(context);
118404
118405               _background = rendererBackground(context);
118406               _features = rendererFeatures(context);
118407               _map = rendererMap(context);
118408               _photos = rendererPhotos(context);
118409
118410               _ui = uiInit(context);
118411
118412               _connection = services.osm;
118413             }
118414
118415             // Set up objects that might need to access properties of `context`. The order
118416             // might matter if dependents make calls to each other. Be wary of async calls.
118417             function initializeDependents() {
118418
118419               if (context.initialHashParams.presets) {
118420                 _mainPresetIndex.addablePresetIDs(new Set(context.initialHashParams.presets.split(',')));
118421               }
118422
118423               if (context.initialHashParams.locale) {
118424                 _mainLocalizer.preferredLocaleCodes(context.initialHashParams.locale);
118425               }
118426
118427               // kick off some async work
118428               _mainLocalizer.ensureLoaded();
118429               _background.ensureLoaded();
118430               _mainPresetIndex.ensureLoaded();
118431
118432               Object.values(services).forEach(service => {
118433                 if (service && typeof service.init === 'function') {
118434                   service.init();
118435                 }
118436               });
118437
118438               _map.init();
118439               _validator.init();
118440               _features.init();
118441               _photos.init();
118442
118443               if (services.maprules && context.initialHashParams.maprules) {
118444                 d3_json(context.initialHashParams.maprules)
118445                   .then(mapcss => {
118446                     services.maprules.init();
118447                     mapcss.forEach(mapcssSelector => services.maprules.addRule(mapcssSelector));
118448                   })
118449                   .catch(() => { /* ignore */ });
118450               }
118451
118452               // if the container isn't available, e.g. when testing, don't load the UI
118453               if (!context.container().empty()) _ui.ensureLoaded();
118454             }
118455           };
118456
118457
118458           return context;
118459         }
118460
118461         // When `debug = true`, we use `Object.freeze` on immutables in iD.
118462         // This is only done in testing because of the performance penalty.
118463         let debug = false;
118464         let d3 = {
118465           customEvent: customEvent,
118466           dispatch:  dispatch,
118467           event:  event,
118468           geoMercator: mercator,
118469           geoProjection: projection,
118470           polygonArea: d3_polygonArea,
118471           polygonCentroid: d3_polygonCentroid,
118472           select: select,
118473           selectAll: selectAll,
118474           timerFlush: timerFlush
118475         };
118476
118477         var iD = /*#__PURE__*/Object.freeze({
118478                 __proto__: null,
118479                 debug: debug,
118480                 d3: d3,
118481                 actionAddEntity: actionAddEntity,
118482                 actionAddMember: actionAddMember,
118483                 actionAddMidpoint: actionAddMidpoint,
118484                 actionAddVertex: actionAddVertex,
118485                 actionChangeMember: actionChangeMember,
118486                 actionChangePreset: actionChangePreset,
118487                 actionChangeTags: actionChangeTags,
118488                 actionCircularize: actionCircularize,
118489                 actionConnect: actionConnect,
118490                 actionCopyEntities: actionCopyEntities,
118491                 actionDeleteMember: actionDeleteMember,
118492                 actionDeleteMultiple: actionDeleteMultiple,
118493                 actionDeleteNode: actionDeleteNode,
118494                 actionDeleteRelation: actionDeleteRelation,
118495                 actionDeleteWay: actionDeleteWay,
118496                 actionDiscardTags: actionDiscardTags,
118497                 actionDisconnect: actionDisconnect,
118498                 actionExtract: actionExtract,
118499                 actionJoin: actionJoin,
118500                 actionMerge: actionMerge,
118501                 actionMergeNodes: actionMergeNodes,
118502                 actionMergePolygon: actionMergePolygon,
118503                 actionMergeRemoteChanges: actionMergeRemoteChanges,
118504                 actionMove: actionMove,
118505                 actionMoveMember: actionMoveMember,
118506                 actionMoveNode: actionMoveNode,
118507                 actionNoop: actionNoop,
118508                 actionOrthogonalize: actionOrthogonalize,
118509                 actionRestrictTurn: actionRestrictTurn,
118510                 actionReverse: actionReverse,
118511                 actionRevert: actionRevert,
118512                 actionRotate: actionRotate,
118513                 actionSplit: actionSplit,
118514                 actionStraightenNodes: actionStraightenNodes,
118515                 actionStraightenWay: actionStraightenWay,
118516                 actionUnrestrictTurn: actionUnrestrictTurn,
118517                 actionReflect: actionReflect,
118518                 actionUpgradeTags: actionUpgradeTags,
118519                 behaviorAddWay: behaviorAddWay,
118520                 behaviorBreathe: behaviorBreathe,
118521                 behaviorDrag: behaviorDrag,
118522                 behaviorDrawWay: behaviorDrawWay,
118523                 behaviorDraw: behaviorDraw,
118524                 behaviorEdit: behaviorEdit,
118525                 behaviorHash: behaviorHash,
118526                 behaviorHover: behaviorHover,
118527                 behaviorLasso: behaviorLasso,
118528                 behaviorOperation: behaviorOperation,
118529                 behaviorPaste: behaviorPaste,
118530                 behaviorSelect: behaviorSelect,
118531                 coreContext: coreContext,
118532                 coreFileFetcher: coreFileFetcher,
118533                 fileFetcher: _mainFileFetcher,
118534                 coreDifference: coreDifference,
118535                 coreGraph: coreGraph,
118536                 coreHistory: coreHistory,
118537                 coreLocalizer: coreLocalizer,
118538                 t: _t,
118539                 localizer: _mainLocalizer,
118540                 prefs: corePreferences,
118541                 coreTree: coreTree,
118542                 coreUploader: coreUploader,
118543                 coreValidator: coreValidator,
118544                 geoExtent: geoExtent,
118545                 geoLatToMeters: geoLatToMeters,
118546                 geoLonToMeters: geoLonToMeters,
118547                 geoMetersToLat: geoMetersToLat,
118548                 geoMetersToLon: geoMetersToLon,
118549                 geoMetersToOffset: geoMetersToOffset,
118550                 geoOffsetToMeters: geoOffsetToMeters,
118551                 geoScaleToZoom: geoScaleToZoom,
118552                 geoSphericalClosestNode: geoSphericalClosestNode,
118553                 geoSphericalDistance: geoSphericalDistance,
118554                 geoZoomToScale: geoZoomToScale,
118555                 geoAngle: geoAngle,
118556                 geoChooseEdge: geoChooseEdge,
118557                 geoEdgeEqual: geoEdgeEqual,
118558                 geoGetSmallestSurroundingRectangle: geoGetSmallestSurroundingRectangle,
118559                 geoHasLineIntersections: geoHasLineIntersections,
118560                 geoHasSelfIntersections: geoHasSelfIntersections,
118561                 geoRotate: geoRotate,
118562                 geoLineIntersection: geoLineIntersection,
118563                 geoPathHasIntersections: geoPathHasIntersections,
118564                 geoPathIntersections: geoPathIntersections,
118565                 geoPathLength: geoPathLength,
118566                 geoPointInPolygon: geoPointInPolygon,
118567                 geoPolygonContainsPolygon: geoPolygonContainsPolygon,
118568                 geoPolygonIntersectsPolygon: geoPolygonIntersectsPolygon,
118569                 geoViewportEdge: geoViewportEdge,
118570                 geoRawMercator: geoRawMercator,
118571                 geoVecAdd: geoVecAdd,
118572                 geoVecAngle: geoVecAngle,
118573                 geoVecCross: geoVecCross,
118574                 geoVecDot: geoVecDot,
118575                 geoVecEqual: geoVecEqual,
118576                 geoVecFloor: geoVecFloor,
118577                 geoVecInterp: geoVecInterp,
118578                 geoVecLength: geoVecLength,
118579                 geoVecLengthSquare: geoVecLengthSquare,
118580                 geoVecNormalize: geoVecNormalize,
118581                 geoVecNormalizedDot: geoVecNormalizedDot,
118582                 geoVecProject: geoVecProject,
118583                 geoVecSubtract: geoVecSubtract,
118584                 geoVecScale: geoVecScale,
118585                 geoOrthoNormalizedDotProduct: geoOrthoNormalizedDotProduct,
118586                 geoOrthoCalcScore: geoOrthoCalcScore,
118587                 geoOrthoMaxOffsetAngle: geoOrthoMaxOffsetAngle,
118588                 geoOrthoCanOrthogonalize: geoOrthoCanOrthogonalize,
118589                 modeAddArea: modeAddArea,
118590                 modeAddLine: modeAddLine,
118591                 modeAddPoint: modeAddPoint,
118592                 modeAddNote: modeAddNote,
118593                 modeBrowse: modeBrowse,
118594                 modeDragNode: modeDragNode,
118595                 modeDragNote: modeDragNote,
118596                 modeDrawArea: modeDrawArea,
118597                 modeDrawLine: modeDrawLine,
118598                 modeMove: modeMove,
118599                 modeRotate: modeRotate,
118600                 modeSave: modeSave,
118601                 modeSelect: modeSelect,
118602                 modeSelectData: modeSelectData,
118603                 modeSelectError: modeSelectError,
118604                 modeSelectNote: modeSelectNote,
118605                 operationCircularize: operationCircularize,
118606                 operationContinue: operationContinue,
118607                 operationCopy: operationCopy,
118608                 operationDelete: operationDelete,
118609                 operationDisconnect: operationDisconnect,
118610                 operationDowngrade: operationDowngrade,
118611                 operationExtract: operationExtract,
118612                 operationMerge: operationMerge,
118613                 operationMove: operationMove,
118614                 operationOrthogonalize: operationOrthogonalize,
118615                 operationPaste: operationPaste,
118616                 operationReflectShort: operationReflectShort,
118617                 operationReflectLong: operationReflectLong,
118618                 operationReverse: operationReverse,
118619                 operationRotate: operationRotate,
118620                 operationSplit: operationSplit,
118621                 operationStraighten: operationStraighten,
118622                 osmChangeset: osmChangeset,
118623                 osmEntity: osmEntity,
118624                 osmNode: osmNode,
118625                 osmNote: osmNote,
118626                 osmRelation: osmRelation,
118627                 osmWay: osmWay,
118628                 QAItem: QAItem,
118629                 osmIntersection: osmIntersection,
118630                 osmTurn: osmTurn,
118631                 osmInferRestriction: osmInferRestriction,
118632                 osmLanes: osmLanes,
118633                 osmOldMultipolygonOuterMemberOfRelation: osmOldMultipolygonOuterMemberOfRelation,
118634                 osmIsOldMultipolygonOuterMember: osmIsOldMultipolygonOuterMember,
118635                 osmOldMultipolygonOuterMember: osmOldMultipolygonOuterMember,
118636                 osmJoinWays: osmJoinWays,
118637                 get osmAreaKeys () { return osmAreaKeys; },
118638                 osmSetAreaKeys: osmSetAreaKeys,
118639                 osmTagSuggestingArea: osmTagSuggestingArea,
118640                 get osmPointTags () { return osmPointTags; },
118641                 osmSetPointTags: osmSetPointTags,
118642                 get osmVertexTags () { return osmVertexTags; },
118643                 osmSetVertexTags: osmSetVertexTags,
118644                 osmNodeGeometriesForTags: osmNodeGeometriesForTags,
118645                 osmOneWayTags: osmOneWayTags,
118646                 osmPavedTags: osmPavedTags,
118647                 osmIsInterestingTag: osmIsInterestingTag,
118648                 osmRoutableHighwayTagValues: osmRoutableHighwayTagValues,
118649                 osmFlowingWaterwayTagValues: osmFlowingWaterwayTagValues,
118650                 osmRailwayTrackTagValues: osmRailwayTrackTagValues,
118651                 presetCategory: presetCategory,
118652                 presetCollection: presetCollection,
118653                 presetField: presetField,
118654                 presetPreset: presetPreset,
118655                 presetManager: _mainPresetIndex,
118656                 presetIndex: presetIndex,
118657                 rendererBackgroundSource: rendererBackgroundSource,
118658                 rendererBackground: rendererBackground,
118659                 rendererFeatures: rendererFeatures,
118660                 rendererMap: rendererMap,
118661                 rendererPhotos: rendererPhotos,
118662                 rendererTileLayer: rendererTileLayer,
118663                 services: services,
118664                 serviceKeepRight: serviceKeepRight,
118665                 serviceImproveOSM: serviceImproveOSM,
118666                 serviceOsmose: serviceOsmose,
118667                 serviceMapillary: serviceMapillary,
118668                 serviceMapRules: serviceMapRules,
118669                 serviceNominatim: serviceNominatim,
118670                 serviceOpenstreetcam: serviceOpenstreetcam,
118671                 serviceOsm: serviceOsm,
118672                 serviceOsmWikibase: serviceOsmWikibase,
118673                 serviceStreetside: serviceStreetside,
118674                 serviceTaginfo: serviceTaginfo,
118675                 serviceVectorTile: serviceVectorTile,
118676                 serviceWikidata: serviceWikidata,
118677                 serviceWikipedia: serviceWikipedia,
118678                 svgAreas: svgAreas,
118679                 svgData: svgData,
118680                 svgDebug: svgDebug,
118681                 svgDefs: svgDefs,
118682                 svgKeepRight: svgKeepRight,
118683                 svgIcon: svgIcon,
118684                 svgGeolocate: svgGeolocate,
118685                 svgLabels: svgLabels,
118686                 svgLayers: svgLayers,
118687                 svgLines: svgLines,
118688                 svgMapillaryImages: svgMapillaryImages,
118689                 svgMapillarySigns: svgMapillarySigns,
118690                 svgMidpoints: svgMidpoints,
118691                 svgNotes: svgNotes,
118692                 svgMarkerSegments: svgMarkerSegments,
118693                 svgOpenstreetcamImages: svgOpenstreetcamImages,
118694                 svgOsm: svgOsm,
118695                 svgPassiveVertex: svgPassiveVertex,
118696                 svgPath: svgPath,
118697                 svgPointTransform: svgPointTransform,
118698                 svgPoints: svgPoints,
118699                 svgRelationMemberTags: svgRelationMemberTags,
118700                 svgSegmentWay: svgSegmentWay,
118701                 svgStreetside: svgStreetside,
118702                 svgTagClasses: svgTagClasses,
118703                 svgTagPattern: svgTagPattern,
118704                 svgTouch: svgTouch,
118705                 svgTurns: svgTurns,
118706                 svgVertices: svgVertices,
118707                 uiFieldDefaultCheck: uiFieldCheck,
118708                 uiFieldOnewayCheck: uiFieldCheck,
118709                 uiFieldCheck: uiFieldCheck,
118710                 uiFieldMultiCombo: uiFieldCombo,
118711                 uiFieldNetworkCombo: uiFieldCombo,
118712                 uiFieldSemiCombo: uiFieldCombo,
118713                 uiFieldTypeCombo: uiFieldCombo,
118714                 uiFieldCombo: uiFieldCombo,
118715                 uiFieldUrl: uiFieldText,
118716                 uiFieldIdentifier: uiFieldText,
118717                 uiFieldNumber: uiFieldText,
118718                 uiFieldTel: uiFieldText,
118719                 uiFieldEmail: uiFieldText,
118720                 uiFieldText: uiFieldText,
118721                 uiFieldAccess: uiFieldAccess,
118722                 uiFieldAddress: uiFieldAddress,
118723                 uiFieldCycleway: uiFieldCycleway,
118724                 uiFieldLanes: uiFieldLanes,
118725                 uiFieldLocalized: uiFieldLocalized,
118726                 uiFieldMaxspeed: uiFieldMaxspeed,
118727                 uiFieldStructureRadio: uiFieldRadio,
118728                 uiFieldRadio: uiFieldRadio,
118729                 uiFieldRestrictions: uiFieldRestrictions,
118730                 uiFieldTextarea: uiFieldTextarea,
118731                 uiFieldWikidata: uiFieldWikidata,
118732                 uiFieldWikipedia: uiFieldWikipedia,
118733                 uiFields: uiFields,
118734                 uiIntro: uiIntro,
118735                 uiPanelBackground: uiPanelBackground,
118736                 uiPanelHistory: uiPanelHistory,
118737                 uiPanelLocation: uiPanelLocation,
118738                 uiPanelMeasurement: uiPanelMeasurement,
118739                 uiInfoPanels: uiInfoPanels,
118740                 uiPaneBackground: uiPaneBackground,
118741                 uiPaneHelp: uiPaneHelp,
118742                 uiPaneIssues: uiPaneIssues,
118743                 uiPaneMapData: uiPaneMapData,
118744                 uiPanePreferences: uiPanePreferences,
118745                 uiSectionBackgroundDisplayOptions: uiSectionBackgroundDisplayOptions,
118746                 uiSectionBackgroundList: uiSectionBackgroundList,
118747                 uiSectionBackgroundOffset: uiSectionBackgroundOffset,
118748                 uiSectionChanges: uiSectionChanges,
118749                 uiSectionDataLayers: uiSectionDataLayers,
118750                 uiSectionEntityIssues: uiSectionEntityIssues,
118751                 uiSectionFeatureType: uiSectionFeatureType,
118752                 uiSectionMapFeatures: uiSectionMapFeatures,
118753                 uiSectionMapStyleOptions: uiSectionMapStyleOptions,
118754                 uiSectionOverlayList: uiSectionOverlayList,
118755                 uiSectionPhotoOverlays: uiSectionPhotoOverlays,
118756                 uiSectionPresetFields: uiSectionPresetFields,
118757                 uiSectionPrivacy: uiSectionPrivacy,
118758                 uiSectionRawMemberEditor: uiSectionRawMemberEditor,
118759                 uiSectionRawMembershipEditor: uiSectionRawMembershipEditor,
118760                 uiSectionRawTagEditor: uiSectionRawTagEditor,
118761                 uiSectionSelectionList: uiSectionSelectionList,
118762                 uiSectionValidationIssues: uiSectionValidationIssues,
118763                 uiSectionValidationOptions: uiSectionValidationOptions,
118764                 uiSectionValidationRules: uiSectionValidationRules,
118765                 uiSectionValidationStatus: uiSectionValidationStatus,
118766                 uiSettingsCustomBackground: uiSettingsCustomBackground,
118767                 uiSettingsCustomData: uiSettingsCustomData,
118768                 uiInit: uiInit,
118769                 uiAccount: uiAccount,
118770                 uiAttribution: uiAttribution,
118771                 uiChangesetEditor: uiChangesetEditor,
118772                 uiCmd: uiCmd,
118773                 uiCombobox: uiCombobox,
118774                 uiCommit: uiCommit,
118775                 uiCommitWarnings: uiCommitWarnings,
118776                 uiConfirm: uiConfirm,
118777                 uiConflicts: uiConflicts,
118778                 uiContributors: uiContributors,
118779                 uiCurtain: uiCurtain,
118780                 uiDataEditor: uiDataEditor,
118781                 uiDataHeader: uiDataHeader,
118782                 uiDisclosure: uiDisclosure,
118783                 uiEditMenu: uiEditMenu,
118784                 uiEntityEditor: uiEntityEditor,
118785                 uiFeatureInfo: uiFeatureInfo,
118786                 uiFeatureList: uiFeatureList,
118787                 uiField: uiField,
118788                 uiFieldHelp: uiFieldHelp,
118789                 uiFlash: uiFlash,
118790                 uiFormFields: uiFormFields,
118791                 uiFullScreen: uiFullScreen,
118792                 uiGeolocate: uiGeolocate,
118793                 uiImproveOsmComments: uiImproveOsmComments,
118794                 uiImproveOsmDetails: uiImproveOsmDetails,
118795                 uiImproveOsmEditor: uiImproveOsmEditor,
118796                 uiImproveOsmHeader: uiImproveOsmHeader,
118797                 uiInfo: uiInfo,
118798                 uiInspector: uiInspector,
118799                 uiIssuesInfo: uiIssuesInfo,
118800                 uiKeepRightDetails: uiKeepRightDetails,
118801                 uiKeepRightEditor: uiKeepRightEditor,
118802                 uiKeepRightHeader: uiKeepRightHeader,
118803                 uiLasso: uiLasso,
118804                 uiLoading: uiLoading,
118805                 uiMapInMap: uiMapInMap,
118806                 uiModal: uiModal,
118807                 uiNotice: uiNotice,
118808                 uiNoteComments: uiNoteComments,
118809                 uiNoteEditor: uiNoteEditor,
118810                 uiNoteHeader: uiNoteHeader,
118811                 uiNoteReport: uiNoteReport,
118812                 uiPopover: uiPopover,
118813                 uiPresetIcon: uiPresetIcon,
118814                 uiPresetList: uiPresetList,
118815                 uiRestore: uiRestore,
118816                 uiScale: uiScale,
118817                 uiSidebar: uiSidebar,
118818                 uiSourceSwitch: uiSourceSwitch,
118819                 uiSpinner: uiSpinner,
118820                 uiSplash: uiSplash,
118821                 uiStatus: uiStatus,
118822                 uiSuccess: uiSuccess,
118823                 uiTagReference: uiTagReference,
118824                 uiToggle: uiToggle,
118825                 uiTooltip: uiTooltip,
118826                 uiVersion: uiVersion,
118827                 uiViewOnOSM: uiViewOnOSM,
118828                 uiViewOnKeepRight: uiViewOnKeepRight,
118829                 uiZoom: uiZoom,
118830                 utilAesEncrypt: utilAesEncrypt,
118831                 utilAesDecrypt: utilAesDecrypt,
118832                 utilArrayChunk: utilArrayChunk,
118833                 utilArrayDifference: utilArrayDifference,
118834                 utilArrayFlatten: utilArrayFlatten,
118835                 utilArrayGroupBy: utilArrayGroupBy,
118836                 utilArrayIdentical: utilArrayIdentical,
118837                 utilArrayIntersection: utilArrayIntersection,
118838                 utilArrayUnion: utilArrayUnion,
118839                 utilArrayUniq: utilArrayUniq,
118840                 utilArrayUniqBy: utilArrayUniqBy,
118841                 utilAsyncMap: utilAsyncMap,
118842                 utilCleanTags: utilCleanTags,
118843                 utilCombinedTags: utilCombinedTags,
118844                 utilDeepMemberSelector: utilDeepMemberSelector,
118845                 utilDetect: utilDetect,
118846                 utilDisplayName: utilDisplayName,
118847                 utilDisplayNameForPath: utilDisplayNameForPath,
118848                 utilDisplayType: utilDisplayType,
118849                 utilDisplayLabel: utilDisplayLabel,
118850                 utilEntityRoot: utilEntityRoot,
118851                 utilEditDistance: utilEditDistance,
118852                 utilEntitySelector: utilEntitySelector,
118853                 utilEntityOrMemberSelector: utilEntityOrMemberSelector,
118854                 utilEntityOrDeepMemberSelector: utilEntityOrDeepMemberSelector,
118855                 utilFastMouse: utilFastMouse,
118856                 utilFunctor: utilFunctor,
118857                 utilGetAllNodes: utilGetAllNodes,
118858                 utilGetSetValue: utilGetSetValue,
118859                 utilHashcode: utilHashcode,
118860                 utilHighlightEntities: utilHighlightEntities,
118861                 utilKeybinding: utilKeybinding,
118862                 utilNoAuto: utilNoAuto,
118863                 utilObjectOmit: utilObjectOmit,
118864                 utilPrefixCSSProperty: utilPrefixCSSProperty,
118865                 utilPrefixDOMProperty: utilPrefixDOMProperty,
118866                 utilQsString: utilQsString,
118867                 utilRebind: utilRebind,
118868                 utilSafeClassName: utilSafeClassName,
118869                 utilSetTransform: utilSetTransform,
118870                 utilSessionMutex: utilSessionMutex,
118871                 utilStringQs: utilStringQs,
118872                 utilTagDiff: utilTagDiff,
118873                 utilTagText: utilTagText,
118874                 utilTiler: utilTiler,
118875                 utilTotalExtent: utilTotalExtent,
118876                 utilTriggerEvent: utilTriggerEvent,
118877                 utilUnicodeCharsCount: utilUnicodeCharsCount,
118878                 utilUnicodeCharsTruncated: utilUnicodeCharsTruncated,
118879                 utilUniqueDomId: utilUniqueDomId,
118880                 utilWrap: utilWrap,
118881                 validationAlmostJunction: validationAlmostJunction,
118882                 validationCloseNodes: validationCloseNodes,
118883                 validationCrossingWays: validationCrossingWays,
118884                 validationDisconnectedWay: validationDisconnectedWay,
118885                 validationFormatting: validationFormatting,
118886                 validationHelpRequest: validationHelpRequest,
118887                 validationImpossibleOneway: validationImpossibleOneway,
118888                 validationIncompatibleSource: validationIncompatibleSource,
118889                 validationMaprules: validationMaprules,
118890                 validationMismatchedGeometry: validationMismatchedGeometry,
118891                 validationMissingRole: validationMissingRole,
118892                 validationMissingTag: validationMissingTag,
118893                 validationOutdatedTags: validationOutdatedTags,
118894                 validationPrivateData: validationPrivateData,
118895                 validationSuspiciousName: validationSuspiciousName,
118896                 validationUnsquareWay: validationUnsquareWay
118897         });
118898
118899         // polyfill requestIdleCallback
118900         window.requestIdleCallback = window.requestIdleCallback ||
118901             function(cb) {
118902                 var start = Date.now();
118903                 return window.requestAnimationFrame(function() {
118904                     cb({
118905                         didTimeout: false,
118906                         timeRemaining: function() {
118907                             return Math.max(0, 50 - (Date.now() - start));
118908                         }
118909                     });
118910                 });
118911             };
118912
118913         window.cancelIdleCallback = window.cancelIdleCallback ||
118914             function(id) {
118915                 window.cancelAnimationFrame(id);
118916             };
118917         window.iD = iD;
118918
118919 }());
118920 //# sourceMappingURL=iD.js.map